Skip Menu |
Report information
Id: 128818
Status: open
Priority: 0/
Queue: perl6

Owner: Nobody
Requestors: zefram [at] fysh.org
Cc:
AdminCc:

Severity: (no value)
Tag: Bug
Platform: (no value)
Patch Status: (no value)
VM: (no value)



Date: Tue, 2 Aug 2016 17:56:25 +0100
Subject: [BUG] sprintf %f bogus rounding
To: rakudobug [...] perl.org
From: Zefram <zefram [...] fysh.org>
Download (untitled) / with headers
text/plain 296b
Show quoted text
> (1180591620717411303424.0e0).Int
1180591620717411303424 Show quoted text
> sprintf("%f", 1180591620717411303424.0e0)
1180591620717410000000.000000 sprintf %f is not showing the true value of this Num, which it should. The .Int coercion is correct, and shows that the true value is actually available. -zefram
To: perl6-compiler [...] perl.org
From: "Patrick R. Michaud" <pmichaud [...] pobox.com>
Subject: Re: [perl #128818] [BUG] sprintf %f bogus rounding
Date: Tue, 2 Aug 2016 13:21:48 -0500
Download (untitled) / with headers
text/plain 655b
On Tue, Aug 02, 2016 at 09:56:38AM -0700, Zefram wrote: Show quoted text
> > (1180591620717411303424.0e0).Int
> 1180591620717411303424
> > sprintf("%f", 1180591620717411303424.0e0)
> 1180591620717410000000.000000 > > sprintf %f is not showing the true value of this Num, which it should. > The .Int coercion is correct, and shows that the true value is actually > available.
Nums hold only 15-17 digits of precision, so sprintf("%f") likely has this one correct. In particular, the true value is *not* always available, as illustrated by: Show quoted text
> (1180591620717411303424.0e0).Int
1180591620717411303424 Show quoted text
> (1180591620717411303434.0e0).Int
1180591620717411303424 Pm
Date: Tue, 2 Aug 2016 19:46:20 +0100
Subject: Re: [perl #128818] [BUG] sprintf %f bogus rounding
To: "Patrick R. Michaud via RT" <perl6-bugs-followup [...] perl.org>
From: Zefram <zefram [...] fysh.org>
Download (untitled) / with headers
text/plain 1.2k
Patrick R. Michaud via RT wrote: Show quoted text
>Nums hold only 15-17 digits of precision, so sprintf("%f") >likely has this one correct.
No, it does not have this correct. The value represented in the Num is exactly 2**70, and the .Int accurately reflects that. It is the sprintf that is failing to show the Num's value. Show quoted text
> 2**70
1180591620717411303424 Show quoted text
> (2e0**70).Int
1180591620717411303424 Show quoted text
> sprintf("%f", 2e0**70)
1180591620717410000000.000000 Show quoted text
>In particular, the true value is *not* always available,
By "true value" I meant the value represented in floating point. I did not mean the value directly represented by the decimal literal. Your example here shows unavoidable (and correctly performed) rounding in the decimal->float conversion. In my example I deliberately used a value that could be exactly represented in floating point, so that this decimal->float rounding would not arise. It is not the issue here. It's sounding, in this and [perl #128817], rather as though you're thinking about floating point precision entirely in decimal terms. It is as if you think Num implements decimal floating point, though empirically it clearly exhibits the arithmetic behaviour of ordinary binary floating point. Is there perhaps some underlying design decision for decimal floating point that hasn't been carried through? -zefram
From: "Patrick R. Michaud" <pmichaud [...] pobox.com>
To: Zefram <zefram [...] fysh.org>
Subject: Re: [perl #128818] [BUG] sprintf %f bogus rounding
Date: Tue, 2 Aug 2016 14:14:55 -0500
CC: "Patrick R. Michaud via RT" <perl6-bugs-followup [...] perl.org>
Download (untitled) / with headers
text/plain 879b
On Tue, Aug 02, 2016 at 07:46:20PM +0100, Zefram wrote: Show quoted text
> > sprintf("%f", 2e0**70)
> 1180591620717410000000.000000 >
> >In particular, the true value is *not* always available,
> > By "true value" I meant the value represented in floating point.
My apologies, I did not catch this meaning of "true value" from your posts. You've correctly deduced that I've been referring to the mathematically pure value represented by the decimal literal in source code, and not the value that results from its conversion to floating point. I agree that having sprintf("%f") automatically truncate with zeros after 15 digits of precision is perhaps not the best approach here. I think it should perhaps be available to at least 17 places, and possibly even more in examples like 2**70. But that's a language specification call that someone outside of Rakudo probably needs to make. Pm
To: "Patrick R. Michaud via RT" <perl6-bugs-followup [...] perl.org>
Subject: Re: [perl #128818] [BUG] sprintf %f bogus rounding
Date: Tue, 2 Aug 2016 20:51:00 +0100
From: Zefram <zefram [...] fysh.org>
Download (untitled) / with headers
text/plain 1.6k
Patrick R. Michaud wrote: Show quoted text
> But that's a language >specification call that someone outside of Rakudo probably needs >to make.
doc/Type/Str.pod (which I checked before reporting the bug) says it's "mostly identical to the C library sprintf function", and doesn't describe any relevant derogation from the C definition. C practice is to show the value actually represented by the floating-point value, correctly rounded to the output precision. If you want the formal definition, the most up-to-date version of the C standard that I have (not very recent, but I don't expect this to have changed) says # f,F A double argument representing a (finite) floating-point number # is converted to decimal notation in the style "[-]ddd.ddd", # where the number of digits after the decimal-point character # is equal to the precision specification. If the precision is # missing, it is taken as 6; if the precision is zero and the # "#" flag is not specified, no decimal-point character appears. # If a decimal-point character appears, at least one digit # appears before it. The value is rounded to the appropriate # number of digits. This is slightly less clear than I expected: there's a tiny bit of room to argue about what the "appropriate number of digits" is. In context I think it's clear enough that the appropriate number is the number of digits following the decimal point, which the standard has just spent half the paragraph describing. The standard does go on to make clear that it doesn't require the rounding to be correct, though. -zefram
To: "Patrick R. Michaud via RT" <perl6-bugs-followup [...] perl.org>
Subject: Re: [perl #128818] [BUG] sprintf %f bogus rounding
From: Zefram <zefram [...] fysh.org>
Date: Wed, 3 Aug 2016 23:32:27 +0100
Download (untitled) / with headers
text/plain 1.1k
A related case, now that I've actually looked at the implementation: Show quoted text
> (2251799813685577e0 * 2e0**-51) - 1
1.46105350040671e-13 Show quoted text
> sprintf("%.13f", 2251799813685577e0 * 2e0**-51)
1.0000000000002 The Num value in this case is exactly 2251799813685577/2251799813685248, or 1.000000000000146105350040670600719749927520751953125. (Subtracting 1 correctly shows a few of these digits.) The correct rounding to 13 decimal places is 1.0000000000001. There is plenty of precision left in the float format after these decimal places: one unit in that last output place corresponds to over 450 ulp in the float format. What's going on is that the implementation is first converting to decimal with its default of 15 total digits, getting a correctly-rounded 1.00000000000015, and then rounding *that* to the output length, getting 1.0000000000002. This is double rounding, a classic type of mistake in numerical computation. You need to perform rounding only once, straight to the output length: either during the binary->decimal conversion, or after a binary->decimal conversion that produced all the digits necessary to represent the value exactly. -zefram
To: "Patrick R. Michaud via RT" <perl6-bugs-followup [...] perl.org>
Subject: Re: [perl #128818] [BUG] sprintf %f bogus rounding
Date: Fri, 12 Aug 2016 22:46:39 +0100
From: Zefram <zefram [...] fysh.org>
Attached is a straightforward implementation of the floating-point printf conversions. I believe it to be correct. In addition to the decimal %e, %f, and %g, I did the rather useful hexadecimal %a, thus far entirely missing from Rakudo's sprintf. I needed some supporting floating-point utilities, for which I translated most of my Perl 5 module Data::Float (including some parts not actually used by the formatting code). Many of these constants and functions really ought to be made available in Rakudo in some form, particularly the IEEE 754r standard functions such as nextafter(). All of them would gain a lot of performance from a C-level implementation that knows the exact layout of the floating-point type; the code here is clunky but portable. An observation: testing floating-point code is substantially impeded by not having reliable means of inputting and outputting floating-point values. In this case, working on output code, I was impeded by some problems with Rakudo's decimal->float conversions on input, hence the handful of recent bug reports from me on that subject. (This work also produced a slew of bug reports about Perl 5's printf %a.) In investigating and reporting those bugs I was impeded by the lack of good floating-point output facilities in Rakudo, hence the less-than-obvious ways of examining Num values in those reports. Input and output facilities support each other. A consequent suggestion: rather than working on decimal floating-point I/O straight away, as a matter of priority you should add hexadecimal floating-point input and output. Currently you have no hex output, and only some unadvertised and limited input (no exponent handling) in Str.Num. The hex is much easier to get right than the decimal, because the radices are commensurable. Once hex I/O is available, it's good for all kinds of debugging purposes, for injecting test values and examining results. Having straightforward and reliable I/O makes all other floating-point issues much easier to approach. Hex would also be an easier way to solve [perl #128819] than anything relying on decimal. -zefram
Download float_print.pl6
text/plain 10.3k

Message body is not shown because sender requested not to inline it.



This service is sponsored and maintained by Best Practical Solutions and runs on Perl.org infrastructure.

For issues related to this RT instance (aka "perlbug"), please contact perlbug-admin at perl.org