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

Reply via email to