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); }