Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Floating point anomalies #6594

Closed
p6rt opened this issue Oct 11, 2017 · 15 comments
Closed

Floating point anomalies #6594

p6rt opened this issue Oct 11, 2017 · 15 comments
Labels

Comments

@p6rt
Copy link

p6rt commented Oct 11, 2017

Migrated from rt.perl.org#132268 (status was 'resolved')

Searchable as RT132268$

@p6rt
Copy link
Author

p6rt commented Oct 11, 2017

From sisyphus1@optusnet.com.au

Hi,
Some anomalous rounding behaviour on Ubuntu-16.04 was noted at
http://www.perlmonks.org/?node_id=1200326

Here are the specifics​:

$ perl6 --version
This is Rakudo version 2017.07 built on MoarVM version 2017.07
implementing Perl 6.c.

=======
Issue 1

$ perl6 -e 'say Int(2e25);'
20000000000000001811939328
$ perl6 -e 'say Int(20e24);'
19999999999999997516972032

That seems odd because 2e25 and 20e24 are exactly equivalent.

=======
Issue 2

$ perl6 -e 'say "WTF" if 1.000000000000001e0 == 1e0;'
WTF

That seems odd because 1.000000000000001e0 and 1e0 are quite different
double precision values. According to perl5​:

$ perl -le 'print scalar reverse unpack "h*", pack "d<",
1.000000000000001e0;'
3ff0000000000005
$ perl -le 'print scalar reverse unpack "h*", pack "d<", 1e0;'
3ff0000000000000

Is it the intention of the perl6 developers that such discrepancies will be
addressed ?

Cheers,
Rob

@p6rt
Copy link
Author

p6rt commented Oct 11, 2017

From @zoffixznet

On Wed, 11 Oct 2017 04​:25​:32 -0700, sisyphus wrote​:

Is it the intention of the perl6 developers that such discrepancies will be
addressed ?

Eventually, yes. A bit lower on the priorities list at the moment, though.

What you describe looks to be similar to the other issue I have in my private bug stash​:

  say .1e0 + .2e0 == .3e0; # False
  say 1.0e-1 + 2.0e-1 == 3.0e-1; # True;

And a brief look into guts suggests it's to do with the way our Nums are constructed during parsing. I was able to repro the issue with the C version of what we do in our Grammar​: https://glot.io/snippets/eufyogt02g

So yeah, the plan is to eventually address these. If you spot any more inconsistencies and weirdness, please report them.

@p6rt
Copy link
Author

p6rt commented Oct 11, 2017

The RT System itself - Status changed from 'new' to 'open'

@p6rt
Copy link
Author

p6rt commented Oct 12, 2017

From sisyphus1@optusnet.com.au

-----Original Message-----
From​: Zoffix Znet via RT
Sent​: Wednesday, October 11, 2017 11​:09 PM
To​: sisyphus1@​optusnet.com.au
Subject​: [perl #​132268] Floating point anomalies

What you describe looks to be similar to the other issue I have in my
private bug stash​:

say .1e0 + .2e0 == .3e0; # False
say 1.0e-1 + 2.0e-1 == 3.0e-1; # True;

And a brief look into guts suggests it's to do with the way our Nums are
constructed during parsing. I was able to repro the issue with the C
version of what we do in our Grammar​: https://glot.io/snippets/eufyogt02g

Yes, the anomaly with those particular values lies in the assignment of .3e0
versus the assignment of 3.0e-1.
Looks like it's the difference between calculating 3 * 0.1 and 30 * 0.01​:

$ perl -le 'print scalar reverse unpack "h*", pack "d<", 3 * 0.1;'
3fd3333333333334

$ perl -le 'print scalar reverse unpack "h*", pack "d<", 30 * 0.01;'
3fd3333333333333

At least, the calculation of 3.0e-1 produces produces the first value (which
is overstated by one ULP), and the calculation of 0.3e0 produces the second
(which is correct).

So yeah, the plan is to eventually address these.

That's good news.

If you spot any more inconsistencies and weirdness, please report them.

Perl6's printf() function looks a little suspect - though I might be missing
something here.

As with perl5's say function, doubles are rounded to 14 decimal digits of
precision, so we get​:

$ perl6 -e 'say 1.000000000000001e0;'
1

$ perl -E 'say 1.000000000000001e0;'
1

On perl5 we can get to see a more accurate rendition of the base 10 value
using printf()​:

$ perl -e 'printf "%.16e\n", 1.000000000000001e0;'
1.0000000000000011e+000

But that doesn't work on perl6​:

$ perl6 -e 'printf "%.16e\n", 1.000000000000001e0;'
1.0000000000000000e+00

Is there something amiss here ?

Cheers,
Rob

@p6rt
Copy link
Author

p6rt commented Oct 12, 2017

From @geekosaur

On Thu, Oct 12, 2017 at 5​:31 AM, <sisyphus1@​optusnet.com.au> wrote​:

Perl6's printf() function looks a little suspect - though I might be
missing
something here.

As with perl5's say function, doubles are rounded to 14 decimal digits of
precision, so we get​:

$ perl6 -e 'say 1.000000000000001e0;'
1

$ perl -E 'say 1.000000000000001e0;'
1

On perl5 we can get to see a more accurate rendition of the base 10 value

I question your use of 'accurate'. The low bits are *never* accurate.
They're trying to be more 'precise', but the value they have will depend
strongly on how exactly the value was calculated, and there are entirely
reasonable design decisions that can cause them to differ between
implementations.

--
brandon s allbery kf8nh sine nomine associates
allbery.b@​gmail.com ballbery@​sinenomine.net
unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

@p6rt
Copy link
Author

p6rt commented Oct 13, 2017

From sisyphus1@optusnet.com.au

From​: Brandon Allbery
Sent​: Friday, October 13, 2017 4​:15 AM
To​: sisyphus1@​optusnet.com.au
Cc​: Carl Mäsak via RT
Subject​: Re​: [perl #​132268] Floating point anomalies
On Thu, Oct 12, 2017 at 5​:31 AM, <sisyphus1@​optusnet.com.au> wrote​:
Perl6's printf() function looks a little suspect - though I might be missing
something here.

As with perl5's say function, doubles are rounded to 14 decimal digits of
precision

To correct that assertion, they both of course round to 15 decimal digits of
precision.

I question your use of 'accurate'. The low bits are *never* accurate.
They're trying to be more 'precise', but the value they have will depend
strongly on how exactly the value was calculated, and there are entirely
reasonable design decisions that can cause them to differ between
implementations.

I don't think there's anything "reasonable" about a printf() implementation
that tells me​:

$ perl6 -e 'printf "%.16e\n", Num(sqrt(3e0));'
1.7320508075688800e+00

Perl6 knows quite well that a double assigned a value of
1.7320508075688800e+00 is not equivalent to the double returned by
Num(sqrt(3e0)) :

$ perl6 -e 'say "not equivalent" if 1.7320508075688800e0 != Num(sqrt(3e0));'
not equivalent

Furthermore, perl6 also knows quite well that a double assigned the value
1.7320508075688772e+00 *is* equivalent to the double returned by
Num(sqrt(3e0))​:

$ perl6 -e 'say "ok" if 1.7320508075688772e0 == Num(sqrt(3e0));'
ok

It's bad enough that perl5 and perl6 round to 15 decimal digits of
precision, but at least perl5's printf will give me 17 decimal digits when I
ask it to (and I think perl6 should do the same)​:

$ perl -e 'printf "%.16e\n", sqrt(3.0);'
1.7320508075688772e+00

As a feature request, it would also be nice to see "%a" formatting
implemented as that then provides one with the means to see the exact value
of the given double.

Cheers,
Rob

@p6rt
Copy link
Author

p6rt commented Oct 13, 2017

From @geekosaur

On Fri, Oct 13, 2017 at 4​:59 AM, <sisyphus1@​optusnet.com.au> wrote​:

It's bad enough that perl5 and perl6 round to 15 decimal digits of
precision, but at least perl5's printf will give me 17 decimal digits when
I ask it to (and I think perl6 should do the same)​:

I am wondering if youve talked to any Intel FP engineers. 17 decimal digits
sounds like you expect full internal 80-bit precision even if it's not in
an internal register. Good luck.
(gcc does have ways of doing this on sufficiently recent processors. msvc
does NOT. So, you've just demanded a Windows vs. Unix difference be
enshrined in the language?)

--
brandon s allbery kf8nh sine nomine associates
allbery.b@​gmail.com ballbery@​sinenomine.net
unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

@p6rt
Copy link
Author

p6rt commented Oct 13, 2017

From @zoffixznet

On Fri, 13 Oct 2017 05​:15​:58 -0700, allbery.b@​gmail.com wrote​:

On Fri, Oct 13, 2017 at 4​:59 AM, <sisyphus1@​optusnet.com.au> wrote​:

It's bad enough that perl5 and perl6 round to 15 decimal digits of
precision, but at least perl5's printf will give me 17 decimal digits when
I ask it to (and I think perl6 should do the same)​:

I am wondering if youve talked to any Intel FP engineers. 17 decimal digits
sounds like you expect full internal 80-bit precision even if it's not in
an internal register. Good luck.
(gcc does have ways of doing this on sufficiently recent processors. msvc
does NOT. So, you've just demanded a Windows vs. Unix difference be
enshrined in the language?)

Perhaps the discussions about who talked to Intel engineers could be moved somewhere off the RT?

The person fixing this ticket can figure out how much precision we can possibly give, without any luck wished upon those who made a feature request.

@p6rt
Copy link
Author

p6rt commented Oct 20, 2017

From @Grimy

How to reproduce


perl6 -e '$_ = 1e-1 + 2e-1; 3e-1; say $_ == 3e-1'

perl6 -e '$_ = 1e-1 + 2e-1; .3e0; say $_ == 3e-1'

Expected behavior


Both scripts should give the same result. If I understand floating-point
handling correctly, they should both print `False`; but both printing `True`
would also seem valid.

Both scripts should warn about the use of a floating-point literal in sink
context.

Actual behavior


The first script prints `True`. The second script prints `False`.

Both scripts correctly warn​: `Useless use of constant floating-point number
0.3 in sink context (line 1)`.

Version information


This is Rakudo version 2017.09 built on MoarVM version 2017.09.1
implementing Perl 6.c.

This was discussed (shortly) on IRC​:
https://irclog.perlgeek.de/perl6/2017-10-19#i_15324682

@p6rt
Copy link
Author

p6rt commented Oct 20, 2017

From @Grimy

How to reproduce


perl6 -e 'my ($a, $b) = set(1e0), set(1e0 + 4e-15); say $a ~~ $b,
$a.keys »�« $b.keys'

Expected behavior


Prints `False(False)`.

Actual behavior


Prints `True(False)`.

This contradicts the documentation of the Setty ACCEPTS method​:
�Returns True if $other and self contain all the same elements, and no
others.� The sets� elements aren�t equal, or even approximately equal
(�), and yet ACCEPTS (~~) returns `True`.

Note that other set methods show similar behavior​: `1e0 â�� (1e0 +
4e-15)` is the empty set, `set(1e0, 1e0 + 4e-15)` only has one
element�

Version information


This is Rakudo version 2017.09 built on MoarVM version 2017.09.1
implementing Perl 6.c.

@p6rt
Copy link
Author

p6rt commented Oct 21, 2017

From @zoffixznet

On Fri, 20 Oct 2017 08​:02​:12 -0700, victor.adam@​derpymail.org wrote​:

How to reproduce
----------------

perl6 -e 'my ($a, $b) = set(1e0), set(1e0 + 4e-15); say $a ~~ $b,
$a.keys »�« $b.keys'

Expected behavior
-----------------

Prints `False(False)`.

Actual behavior
---------------

Prints `True(False)`.

This contradicts the documentation of the Setty ACCEPTS method​:
�Returns True if $other and self contain all the same elements, and no
others.� The sets� elements aren�t equal, or even approximately equal
(�), and yet ACCEPTS (~~) returns `True`.

Note that other set methods show similar behavior​: `1e0 â�� (1e0 +
4e-15)` is the empty set, `set(1e0, 1e0 + 4e-15)` only has one
element�

Version information
-------------------

This is Rakudo version 2017.09 built on MoarVM version 2017.09.1
implementing Perl 6.c.

By accident, I merged incorrect ticket�

If you're fixing original issue, ignore the stuff about set()s�

For set() issue, it isn't to do with set()s but with .Str and .WHICH on a Num losing
a digit of precision. I see a couple of tickets[^1] for that issue already and I
recall dogbertt++ was trying to fix it awhile back.

  <Zoffix__> m​: dd (1e0).Str, (1e0 + 4e-15).Str
  <camelia> rakudo-moar 765dd6944​: OUTPUT​: «"1"â�¤"1"â�¤Â»
  <Zoffix__> m​: dd (1e0).WHICH, (1e0 + 4e-15).WHICH
  <camelia> rakudo-moar 765dd6944​: OUTPUT​: «ObjAt.new("Num|1")â�¤ObjAt.new("Num|1")â�¤Â»

[1] https://rt-archive.perl.org/perl6/Ticket/Display.html?id=127201
[2] https://rt-archive.perl.org/perl6/Ticket/Display.html?id=127184

@p6rt
Copy link
Author

p6rt commented Oct 21, 2017

From @zoffixznet

On Fri, 20 Oct 2017 07​:15​:41 -0700, victor.adam@​derpymail.org wrote​:

How to reproduce
----------------

perl6 -e '$_ = 1e-1 + 2e-1; 3e-1; say $_ == 3e-1'

perl6 -e '$_ = 1e-1 + 2e-1; .3e0; say $_ == 3e-1'

Expected behavior
-----------------

Both scripts should give the same result. If I understand floating-point
handling correctly, they should both print `False`; but both printing `True`
would also seem valid.

Both scripts should warn about the use of a floating-point literal in sink
context.

Actual behavior
---------------

The first script prints `True`. The second script prints `False`.

Both scripts correctly warn​: `Useless use of constant floating-point number
0.3 in sink context (line 1)`.

Version information
-------------------

This is Rakudo version 2017.09 built on MoarVM version 2017.09.1
implementing Perl 6.c.

This was discussed (shortly) on IRC​:
https://irclog.perlgeek.de/perl6/2017-10-19#i_15324682

This is the same cause as RT#​132268. Merging there.

@p6rt
Copy link
Author

p6rt commented Oct 21, 2017

The RT System itself - Status changed from 'new' to 'open'

@p6rt
Copy link
Author

p6rt commented Mar 26, 2018

From @zoffixznet

On Wed, 11 Oct 2017 04​:25​:32 -0700, sisyphus wrote​:

Hi,
Some anomalous rounding behaviour on Ubuntu-16.04 was noted at
http://www.perlmonks.org/?node_id=1200326

Here are the specifics​:

$ perl6 --version
This is Rakudo version 2017.07 built on MoarVM version 2017.07
implementing Perl 6.c.

=======
Issue 1

$ perl6 -e 'say Int(2e25);'
20000000000000001811939328
$ perl6 -e 'say Int(20e24);'
19999999999999997516972032

That seems odd because 2e25 and 20e24 are exactly equivalent.

=======
Issue 2

$ perl6 -e 'say "WTF" if 1.000000000000001e0 == 1e0;'
WTF

That seems odd because 1.000000000000001e0 and 1e0 are quite different
double precision values. According to perl5​:

$ perl -le 'print scalar reverse unpack "h*", pack "d<",
1.000000000000001e0;'
3ff0000000000005
$ perl -le 'print scalar reverse unpack "h*", pack "d<", 1e0;'
3ff0000000000000

Is it the intention of the perl6 developers that such discrepancies will be
addressed ?

Cheers,
Rob

Thank you for the report. All the issues mentioned in the ticket are now fixed.

- The mentioned printf issues look to have been resolved as well,
  even though no work to printf itself was done.
- Somehow fixing Num stringification alone fixed the
  `1.000000000000001e0 == 1e0` bug. That was surprising and I filed
  rakudo/rakudo#1647 to investigate that further
- Support for '%a' in sprintf was mentioned in the ticket, but I suggest
  if there's real interest for that format, a separate ticket to be opened
- There a Num-involved precision issue with drift in Str->Num->Str chains,
  filed as rakudo/rakudo#1651

Fix​: MoarVM/MoarVM@067c0594103a025
  MoarVM/MoarVM@8841c4241b4faa8
  MoarVM/MoarVM@af2eb8a7f7d4344
  MoarVM/MoarVM@4d3fc2818d0032b
  rakudo/rakudo@8422d7b4e23678b
  rakudo/rakudo@a2a2a745c4242d1
Test​: Raku/roast@01d02dafb6
  Raku/roast@b6d5364fe0
  Raku/roast@855af84f82
  Raku/roast@f4a6c635f4
  Raku/roast@d1faf1d049

@p6rt p6rt closed this as completed Mar 26, 2018
@p6rt
Copy link
Author

p6rt commented Mar 26, 2018

@zoffixznet - Status changed from 'open' to 'resolved'

@p6rt p6rt added the math label Jan 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant