On Fri, Mar 2, 2018 at 4:33 PM, Jim Avera <jim.av...@gmail.com> wrote: > Hello, > > Using Rakudo 2018.01: > > my Rat $rat-from-literal = 1.23456789; > my Rat $rat-from-str = "1.23456789".Rat; > my Real $real = 1.23456789e0; > my Rat $rat-from-real = $real.Rat; > > say $rat-from-literal.nude; # (123456789 100000000) > say $rat-from-literal.Str; # 1.23456789 > say $real.Str; # 1.23456789 > say $rat-from-str.Str; # 1.23456789 > say $rat-from-real.Str; # 1.234568 !?! > say $rat-from-real.nude; # (100 81) > > From a user perspective, it's surprising that converting a Real to a Rat is > by default not as precise as the compiler itself when it converts a literal > to a Rat.
A Rat is a Real. The Real role only exists so that you can talk about Numerics while excluding Complex numbers. You are talking about floating point numbers which are of type Num. If you change the epsilon to 1/100000000 they start to return what you expect > (1.23456789e0,1.2345678e0)».Rat(1/100000000) (1.23456789 1.2345678) > (1.23456789e0,1.2345678e0)».Rat(1/100000000)».nude».join: '/' (1356579/1098829 150479/121888) > In particular, if the exact result can be represented in 64 bits (32-bit > numerator & denom), throwing away precision when converting from a 64-bit > Real has no obvious practical benefit. That is, throwing away precision > does not save any memory (assuming Rat uses at least 32+32 -- which is just > my assumption and might be wrong). The Rat type uses up to a 64 bit unsigned integer as it's denominator, the numerator is of arbitrary precision. > As a programmer, I do not want Perl to throw away information without > express permission, unless there is a practical necessity to do so, such as > avoiding exploding memory or computation. You are asking it to throw out the knowledge that it comes from a Num. > > --- > > I think the underlying problem is that Real to Rat conversion precision is > controlled by an "epsilon" value which defaults to 1.0e-6 (see > https://docs.perl6.org/routine/Rat). > > One could argue with the choice of default, but there may not exist an > appropriate default which always DWIM! > > The reason is that I as a programmer don't actually want to limit precision > per se; I only want to limit memory consumed by the result, and perhaps the > cpu time needed to operate on that result. > > --- > > A solution might be to instead provide a pragmatic, rather than mathematical > parameter: > > :$numbits = 64 > > This would say to keep as much precision as possible while making the result > fit in 64 bits. For example 2.147483647e0.Rat would result in > 2147483647/1000000000 instead of 4310/2007. The Num type basically has a denominator which is based on 2s, not 10s. So the runtime has no idea that you expected it to have a denominator of 1000000000 vs 1052513. This number doesn't start stringifying to what you expect until it gets an epsilon of 1/100000000000000 > $_ = 2.147483647e0.Rat(1/100000000000000) 2.147483647 > .nude.join: '/' 22602551/1052513 > If a mathematical epsilon is really what some programmers want (I'm > doubtful), then both options could be provided: > > multi method Rat(Real:D: Real $epsilon) # no default, used only if > user really wants it > multi method Rat(Real:D: Int $maxbits = 64) # provides the default > behavior > > I would appreciate any comments/explanations/insight on this topic! Let's say that .Rat converts a Num to every last bit of precision it can. The result could be so accurate that as soon as you do just about any operation to it, it goes beyond what can be held in a Rat and becomes a Num. (depending on the value) For every one of the examples you have, it would do what you expect if you converted to a Str before converting to a Rat. > 1.23456789e0.Str.Rat 1.23456789 > 2.147483647e0.Str.Rat 2.147483647 At any rate the reason Real exists is so that you can accept any of Rat / Num / Int There could be a better default behaviour, but you haven't detailed an algorithm to do it.