Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Help me make a test case for Math::BigFloat

by fizbin (Chaplain)
on Mar 06, 2006 at 19:15 UTC ( [id://534734]=perlquestion: print w/replies, xml ) Need Help??

fizbin has asked for the wisdom of the Perl Monks concerning the following question:

The following code (when run without any arguments, so that the default @ARGV is picked up below) triggers some kind of strange bizarre bug in Math::BigFloat. It appears that I'm creating a value that is both 1 and 0 at the same time. Can someone help make it shorter into a test case I can submit?
#!perl use bignum; # This is Stirling's approximation for log(n!), but # without the "-n" piece that cancels out below in any # case. sub xe{ $_[0]*log($_[0])+log(atan2(1,1)*8*$_[0])/2 + 1/(12*$_[0]) - 1/(360*($_[0]**3)) + 1/(1260*($_[0]**5)); } if (!@ARGV) { @ARGV = qw{ 2**96 3*(10**6) 1*(10**6) }; } my($m,$n,$r)=map {eval "use bignum;$_"}@ARGV; my $e=exp(xe($m-$n)+xe($m-$r)-xe($m)-xe($m-$n-$r)); printf "%3.5g %3.5g %3.5g\n",$e,1-$e,(1-"$e");

The bug is that the numbers printed in the last line don't make sense. Somehow, both $e and 1-$e print as "1", though 1-"$e" properly prints as "0". (The true value of $e should in fact be somewhere between 1-2**(-54) and 1)

When I use Data::Dumper, then $e is shown as '1' (note the quotes). However, that doesn't really help isolate the bug, as this also creates something that dumps as '1', but doesn't show the "both $e and 1-$e are 1" anomaly:

use bignum; $e = exp(2 ** (-128)); use Data::Dumper; print Dumper($e); print $e,"\n",1-$e,"\n";

My perl environment is perl 5.8.7 on cygwin.

--
@/=map{[/./g]}qw/.h_nJ Xapou cets krht ele_ r_ra/; map{y/X_/\n /;print}map{pop@$_}@/for@/

Replies are listed 'Best First'.
Re: Help me make a test case for Math::BigFloat
by dragonchild (Archbishop) on Mar 06, 2006 at 19:25 UTC
    As a module author, I would love to receive a testcase like that. While it's admirable that you want to make it even smaller, that may be the minimal testcase for this bug. I'd submit it to the author as it stands.

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
      Yeah; I'll admit that once I pulled the code into a pretty format for the SOPW it stopped looking so ugly, convoluted, and in need of beating down into minimalness as it did when it was just four tight lines in my command window following a -e.
      --
      @/=map{[/./g]}qw/.h_nJ Xapou cets krht ele_ r_ra/; map{y/X_/\n /;print}map{pop@$_}@/for@/

        Along those lines, I'd suggest streamlining it even more and submitting it as a test file (i.e. use Test::More instead of print. For example:

        use strict; use warnings; use Test::More tests => 3; use bignum; sub xe{ $_[0]*log($_[0])+log(atan2(1,1)*8*$_[0])/2 + 1/(12*$_[0]) - 1/(360*($_[0]**3)) + 1/(1260*($_[0]**5)); } my ($m, $n, $r) = ( 2**96, 3*(10**6), 1*(10**6) ); my $e=exp(xe($m-$n)+xe($m-$r)-xe($m)-xe($m-$n-$r)); is( $e , 1, '$e should equal 1' ); is( 1-$e , 0, '1-$e should equal 0' ); is( 1-"$e" , 0, '1-"$e" should equal 0' );

        Gives:

        1..3 ok 1 - $e should equal 1 not ok 2 - 1-$e should equal 0 # Failed test (bigtest.pl at line 17) # got: '1' # expected: '0' ok 3 - 1-"$e" should equal 0 # Looks like you failed 1 test of 3.

        That's even easier for a module author to work with.

        -xdg

        Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: Help me make a test case for Math::BigFloat
by BrowserUk (Patriarch) on Mar 06, 2006 at 22:22 UTC

    I don't think this is a bug, just an artifact of the transition between IEEE float constants and bignum objects:

    c:\>perl -MMath::BigFloat -wle"print Math::BigFloat->new(1) - Math::BigFloat->new( .999999999999 +999 )" 0.000000000000001 c:\>perl -MMath::BigFloat -wle"print Math::BigFloat->new(1) - Math::BigFloat->new( .999999999999 +9999 )" 0 c:\>perl -MMath::BigFloat -wle"print Math::BigFloat->new(1) - Math::BigFloat->new('.999999999999 +9999')" 0.0000000000000001 c:\>perl -MMath::BigFloat -wle"print Math::BigFloat->new(1) - Math::BigFloat->new('.999999999999 +99999999999')" 0.00000000000000000000001

    Notice how once you get beyond ~16 decimal places in the constant, the result is wrong. However, if you pass the constant as a string rather than allowing Perl to convert it to a IEEE float before passing it to bignum, the accuracy is retained.

    So the answer is to use string constants to initialise your bignum objects.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      I don't care what I'm initializing something with, there shouldn't be any way to create $ev such that both $ev and 1-$ev print as "1".

      And as for initializing everything as strings, look at this code:

      #!perl use Math::BigFloat; print ((new Math::BigFloat("1")) - exp(new Math::BigFloat("-7") / (new Math::BigFloat("10")**(new Math::BigFloat("17"))))); # Properly prints "0" # (well, something like 7e-17 would be closer, but I'll accept 0)
      Compare it to this code, which is the same but with a use bignum:
      #!perl use bignum; use Math::BigFloat; print ((new Math::BigFloat("1")) - exp(new Math::BigFloat("-7") / (new Math::BigFloat("10")**(new Math::BigFloat("17"))))); # Incorrectly prints "1"
      It would appear then that we have a bug not so much in Math::BigFloat directly, but somewhere in the intersection of Math::BigFloat, bignum, and the magic bignum sprinkles on exp.
      --
      @/=map{[/./g]}qw/.h_nJ Xapou cets krht ele_ r_ra/; map{y/X_/\n /;print}map{pop@$_}@/for@/

        Hmm. There seem to be two interacting problems here.

        1. exp doesn't appear to be overloaded by either bignum or Math::BigFloat?
          #!perl -slw use strict; use overload; #use bignum; use Math::BigFloat; printf "exp(-7e-17): %.17f\n", my $exp = exp( Math::BigFloat->new( '-7 +e-17' ) ); eval{ print overload::Overloaded( $exp ) } or warn $@; print overload::StrVal( $exp ); printf "1-exp(-7-e17): %.17f\n", Math::BigFloat->new("1") - $exp; __END__ C:\test>junk7 exp(-7e-17): 0.99999999999999989 Can't call method "can" without a package or object reference at c:/Pe +rl/lib/overload.pm line 54. 1 1-exp(-7-e17): 0.00000000000000000
        2. And with bignum enabled, Math::BigFloat seems to forget how to do math? At least if the math involves one BigMath object and one normal perl number.
          #!perl -slw use strict; use overload; use bignum; use Math::BigFloat; printf "exp(-7e-17): %.17f\n", my $exp = exp( Math::BigFloat->new( '-7 +e-17' ) ); eval{ print overload::Overloaded( $exp ) } or warn $@; print overload::StrVal( $exp ); printf "1-exp(-7-e17): %.17f\n", Math::BigFloat->new("1") - $exp; __END__ C:\test>junk7 exp(-7e-17): 0.99999999999999989 Can't call method "can" without a package or object reference at c:/Pe +rl/lib/overload.pm line 54. 1 1-exp(-7-e17): 1.00000000000000000

        All of which makes me glad that I rarely need the accuracy beyond 53-bits and when I do, 64-bit ints suffice.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Help me make a test case for Math::BigFloat
by runrig (Abbot) on Mar 06, 2006 at 21:54 UTC
    I think I have a simpler test case:
    use Math::BigFloat; my $one_minus_one = Math::BigInt->new(1) - Math::BigFloat->new(.9999); print "$one_minus_one\n";
    looking at the docs, BigInts are supposed to be upgraded to BigFloats if you use 'use bignum'. The $e is upgraded to a BigFloat, but it is first instantiated as a BigInt, and since is is slightly less than zero, it is instantiated as zero (Note: exp() returns an unblessed regular scalar value slightly less than one). Here is an better test case:
    #!perl use bignum; my $tmp = "-0.00000000000000007573064690121714"; my $e = exp($tmp); printf("%3.20g\n", $e); print "$e ", 1-$e, "\n";
Re: Help me make a test case for Math::BigFloat
by spiritway (Vicar) on Mar 07, 2006 at 06:37 UTC

    Wow, this is mighty strange... I added some more prints of calculations, as so:

    printf "%3.5g %3.5g %3.5g %3.5g\n", ($e+0), (0+$e), (0-$e),($e-0); printf "%3.5g %3.5g %3.5g %3.5g\n", ($e+1), (1+$e), (1-$e),($e-1); printf "%3.5g %3.5g %3.5g %3.5g\n", ($e+$e), ($e-$e), ($e*$e),($e/$e); printf "%3.5g %3.5g %3.5g %3.5g\n", ($e**2), ($e**0), (0/$e),($e/0);

    The output was:

    0 0 0 0 1 1 1 -1 2 0 1 1 0 1 0 0

    In particular, note that the final calculation is ($e/0). The anomaly is more than just $e having an ambiguous value, it seems.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://534734]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (5)
As of 2024-03-28 20:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found