On Thursday 18 September 2008 06:13:30 Patrick R. Michaud (via RT) wrote:
> When generating PIR output (e.g., from the compiler tools), we
> often need to convert a Float value into an equivalent representation
> for PIR. Unfortunately, all of the mechanisms I've looked at for
> doing this lose a lot of precision, which really isn't acceptable.
>
> The prime candidate for this seems to be the C<get_repr> opcode,
> which AFAICT is intended for this purpose. However, it appears to
> provide only about 7 digits of precision.
Yes.
> The approaches I've tried thus far are simple stringification,
> the get_repr opcode, and variations on sprintf formats. Here's
> an example showing the difficulty:
>
> $ cat x.pir
> .sub 'main'
> $N0 = exp 1.0
> 'as_pir'($N0)
> .end
>
> .sub 'as_pir'
> .param pmc value
>
> print "set_p_s : "
> $S0 = value
> say $S0
>
> print "get_repr : "
> $S0 = get_repr value
> say $S0
>
> print "printf %g: "
> $P0 = new 'ResizablePMCArray'
> push $P0, value
> $S0 = sprintf '%g', $P0
> say $S0
>
> .end
>
> $ ./parrot x.pir
> set_p_s : 2.71828
> get_repr : 2.718282
> printf %g: 2.71828
>
> By way of comparison, Perl 5 gives a far more reasonable result:
>
> $ perl -e 'print exp(1),"\n"'
> 2.71828182845905
>
> One approach might be to take whatever algorithm Perl 5 uses for
> stringifying its floats (or something close to it), and adopt that
> for get_repr and/or Float stringification.
How about 15 digits of precision? The attached patch (which requires a
reconfigure) does so. It also drops trailing zeroes, which may or may not be
what you want. It's much more precise though:
set_p_s : 2.71828182845905
get_repr : 2.718281828459045
printf %g: 2.718282
(I used "%.15g" for the format.)
Note that several tests fail as they rely on hard-coded values matching
the %vg and %g formats, with seven digits of precision retaining any trailing
zeroes.
-- c
=== config/auto/format.pm
==================================================================
--- config/auto/format.pm (revision 31407)
+++ config/auto/format.pm (local)
@@ -67,7 +67,7 @@
$nvsize = $floatsize;
if ( $nv eq "double" ) {
$nvsize = $doublesize;
- $nvformat = "%f";
+ $nvformat = "%.15g";
}
elsif ( $nv eq "long double" ) {
=== src/string.c
==================================================================
--- src/string.c (revision 31407)
+++ src/string.c (local)
@@ -2079,7 +2079,7 @@
/* Too damn hard--hand it off to Parrot_sprintf, which'll probably
use the system sprintf anyway, but has gigantic buffers that are
awfully hard to overflow. */
- return Parrot_sprintf_c(interp, "%vg", f);
+ return Parrot_sprintf_c(interp, FLOATVAL_FMT, f);
}