Re: (default) Real->Rat precision should match what compiler uses for literals

2018-03-14 Thread yary
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 Foster  wrote:

> 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

2018-03-07 Thread Solomon Foster
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
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

2018-03-04 Thread yary
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

2018-03-04 Thread Solomon Foster
On Sat, Mar 3, 2018 at 3:32 PM, yary  wrote:

> 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

2018-03-03 Thread Brandon Allbery
Max precision rapidly becomes more memory requires than your computer has.

On Sat, Mar 3, 2018 at 3:32 PM, yary  wrote:

> 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

2018-03-03 Thread yary
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

2018-03-03 Thread yary
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

2018-03-03 Thread yary
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

2018-03-03 Thread Brad Gilbert
On Fri, Mar 2, 2018 at 4:33 PM, Jim Avera  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 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.