Re: (default) Real->Rat precision should match what compiler uses for literals
I want an epsilon that doesn't confuse newbies and which also is efficient. epsilon=1/2**(mantissa bits-1) fits the bill. Why I want this- It would be great to have numbers survive round-trip conversions, when feasible. Specifically I have no need to compare Rats and Nums for equality, but I do often deal with "flat" text files full of metrics, and remotely sourced json, and XML. The data types are sometimes unexpected. -y On Wed, Mar 7, 2018 at 5:16 PM, Solomon Fosterwrote: > On Sun, Mar 4, 2018 at 8:49 AM, yary wrote: > >> In that spirit, I'd expect numeric comparison in general, and epsilon >> specifically, to be set so these return True: >> >> > pi == pi.Rat # Does Num to Rat conversion keep its precision? >> False >> > pi.Str.Num == pi # Does Num survive string round-trip? - Nothing to do >> with epsilon >> False >> >> > Why on earth would you want to do this? > > I mean that quite literally. The only reason I can see for directly > comparing a Num and a Rat for equality is to check and see if the Rat has > the same precision as the Num. In practice, it's well-known you generally > shouldn't use equality tests on floating point numbers. Converting one > side of the equation to a Rat just makes it make even less sense. > > > I've just been playing around with Num to Rat conversion, and here are > some quick notes. > > 1) You can pass 0 as the epsilon for the Rat constructor, which seems to > be equivalent to very very small values of epsilon. > > 2) pi.Rat(0) + exp(1).Rat(0) is a Rat, but pi.Rat(0) + exp(1).Rat(0) + > sin(.2).Rat(0) is a Num. (On the other hand, pi.Rat() + exp(1).Rat() + > sin(.2).Rat() is still a Rat.) > > 3) Remember (I had forgotten!) that Nums can represent numbers much > smaller than a Rat can. 1e-100 is a perfectly reasonable Num, but (were > Rat behaving properly) the closest possible Rat value is 0. > > 4) That said, if you actually do (1e-100).Rat(0), it gives you (1 > 11590289110975991804683608085639452813897813 > 27557747838772170381060813469985856815104). Needless to say, that's not > actually a legal Rat. Surprisingly (to me, anyway) it is accurate to > better than 1e-110. > > 5) Somewhat more distressingly, (1e+100).Rat gives you ( > 11590289110975991804683608085639452813897813 > 27557747838772170381060813469985856815104 1). That's only accurate to > 10**83. Which is to say, it's as accurate as a double gets -- 16-17 > digits. (BTW, that is a legal Rat.) > > I admit don't really know what to do with this. > > -- > Solomon Foster: colo...@gmail.com > HarmonyWare, Inc: http://www.harmonyware.com >
Re: (default) Real->Rat precision should match what compiler uses for literals
On Sun, Mar 4, 2018 at 8:49 AM, yarywrote: > In that spirit, I'd expect numeric comparison in general, and epsilon > specifically, to be set so these return True: > > > pi == pi.Rat # Does Num to Rat conversion keep its precision? > False > > pi.Str.Num == pi # Does Num survive string round-trip? - Nothing to do > with epsilon > False > > Why on earth would you want to do this? I mean that quite literally. The only reason I can see for directly comparing a Num and a Rat for equality is to check and see if the Rat has the same precision as the Num. In practice, it's well-known you generally shouldn't use equality tests on floating point numbers. Converting one side of the equation to a Rat just makes it make even less sense. I've just been playing around with Num to Rat conversion, and here are some quick notes. 1) You can pass 0 as the epsilon for the Rat constructor, which seems to be equivalent to very very small values of epsilon. 2) pi.Rat(0) + exp(1).Rat(0) is a Rat, but pi.Rat(0) + exp(1).Rat(0) + sin(.2).Rat(0) is a Num. (On the other hand, pi.Rat() + exp(1).Rat() + sin(.2).Rat() is still a Rat.) 3) Remember (I had forgotten!) that Nums can represent numbers much smaller than a Rat can. 1e-100 is a perfectly reasonable Num, but (were Rat behaving properly) the closest possible Rat value is 0. 4) That said, if you actually do (1e-100).Rat(0), it gives you (1 1159028911097599180468360808563945281389781327557747838772170381060813469985856815104). Needless to say, that's not actually a legal Rat. Surprisingly (to me, anyway) it is accurate to better than 1e-110. 5) Somewhat more distressingly, (1e+100).Rat gives you (1159028911097599180468360808563945281389781327557747838772170381060813469985856815104 1). That's only accurate to 10**83. Which is to say, it's as accurate as a double gets -- 16-17 digits. (BTW, that is a legal Rat.) I admit don't really know what to do with this. -- Solomon Foster: colo...@gmail.com HarmonyWare, Inc: http://www.harmonyware.com
Re: (default) Real->Rat precision should match what compiler uses for literals
The point of Rats is making Perl6 more correct and less surprising in common cases, such as $ perl6 > 1.1+2.2 3.3 > 1.1+2.2 == 3.3 True > 1.1+2.2 != 3.3 False vs any language using binary floating-point arithmetic DB<1> p 1.1+2.2 3.3 DB<2> p 1.1+2.2 == 3.3 DB<3> p 1.1+2.2 != 3.3 1 In that spirit, I'd expect numeric comparison in general, and epsilon specifically, to be set so these return True: > pi == pi.Rat # Does Num to Rat conversion keep its precision? False > pi.Str.Num == pi # Does Num survive string round-trip? - Nothing to do with epsilon False On the other hand, the original poster Jim and I are both fiddling with the language to see what it's doing with conversions- I'm are not coming across this in an application. What's the greatest value of epsilon that guarantees $x == $x.Rat for all Num $x - and does that epsilon make denominators that are "too big?" My understanding of floating point suggests that we should have an epsilon of 1/2**(mantissa bits-1). On a 64-bit platform that's 1/2**52 - for a 32-bit float that's 1/2**23. Using a 64-bit Rakudo: > pi.Rat(1/2**52) == pi # Just enough precision to match this platform's Num True > pi.Rat(1/2**51) == pi # Epsilon falling a bit short for precision False In terms of correctness, epsilon=1/2**(mantissa bits-1) looks like a winner. Is that acceptable for size and speed? and digressing, how to make "pi.Str.Num == pi" True?
Re: (default) Real->Rat precision should match what compiler uses for literals
On Sat, Mar 3, 2018 at 3:32 PM, yarywrote: > Or instead of 1/2**(32 or 64), re-asking these questions about epsilon: > > " Why so large? > >Why not zero? " > > What's justification for using 1/100,000 vs. something smaller vs. 0 "max > possible precision?" > The problem with using max possible precision is that you will generate a Rat with a large denominator. And when you start doing math with those, the results will usually flip back over to Nums pretty quickly. But if you went through the trouble of converting your number from a Num to a Rat, presumably it's because you wanted to work with Rats, so ending up with a Num again is a frustrating result. I don't remember if I was responsible for 1e-6 as the epsilon or not, but it's quite possible it was me, because in my experience that number is a common 3D tolerance in CAD software (I believe it's the default in ACIS) and I tend to grab it for that purpose. My intuition is that the number should probably be between 1e-6 and 1e-8, but it would be great if someone wanted to put in some research/testing to figure out a better value. It's important to remember that the purpose of Rat in p6 is to dodge around the common pitfalls of floating point numbers in casual use, while degrading to Num to prevent your calculations from crawling to a halt when the denominator gets too big. For real world measures, a double's 15 digits of accuracy is normally overkill. (If I'm doing the math right, if you had a CAD model of the second Death Star in meters, 15 digits would allow you to specify dimensions to the nanometer.) If you actually need unbounded precision, then you should be using FatRats. One thing I think we absolutely should have is a quick and easy way to convert from Num to FatRat with minimal loss of precision. I believe right now the default Num -> FatRat conversion also uses 1e-6 as an epsilon, which seems wrong to me. -- Solomon Foster: colo...@gmail.com HarmonyWare, Inc: http://www.harmonyware.com
Re: (default) Real->Rat precision should match what compiler uses for literals
Max precision rapidly becomes more memory requires than your computer has. On Sat, Mar 3, 2018 at 3:32 PM, yarywrote: > Or instead of 1/2**(32 or 64), re-asking these questions about epsilon: > > " Why so large? > >Why not zero? " > > What's justification for using 1/100,000 vs. something smaller vs. 0 "max > possible precision?" > -- brandon s allbery kf8nh sine nomine associates allber...@gmail.com ballb...@sinenomine.net unix, openafs, kerberos, infrastructure, xmonadhttp://sinenomine.net
Re: (default) Real->Rat precision should match what compiler uses for literals
Still thinking this out. Does the default epsilon influence a Rat == Float comparison? If so, for that purpose, the most useful epsilon is one that maximizes its correctness.
Re: (default) Real->Rat precision should match what compiler uses for literals
Or instead of 1/2**(32 or 64), re-asking these questions about epsilon: " Why so large? Why not zero? " What's justification for using 1/100,000 vs. something smaller vs. 0 "max possible precision?"
Re: (default) Real->Rat precision should match what compiler uses for literals
Zeroing in on one point: > > 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/10 instead of 4310/2007. > > The Num type basically has a denominator which is based on 2s, not 10s. > The proposal is more-or-less expressible as having epsilon = 2/:numbits - presuming that the numerator and denominator are about the same size. >From my POV the default epsilon is fine for practical use, but imprecise enough to have this conversation come up repeatedly. Search this list for "$epsilon = 1.0e-6 feels too big for Rat()" How about setting the default epsilon to 1/2**32 or 1/2**64 and closing that 2015 New Year's Eve post & this one too? -y
Re: (default) Real->Rat precision should match what compiler uses for literals
On Fri, Mar 2, 2018 at 4:33 PM, Jim Averawrote: > 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 1) > 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/1 they start to return what you expect > (1.23456789e0,1.2345678e0)».Rat(1/1) (1.23456789 1.2345678) > (1.23456789e0,1.2345678e0)».Rat(1/1)».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/10 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 10 vs 1052513. This number doesn't start stringifying to what you expect until it gets an epsilon of 1/100 > $_ = 2.147483647e0.Rat(1/100) 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.