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@/
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:
- Does it work?
- Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
| [reply] |
|
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@/
| [reply] [d/l] [select] |
|
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.
| [reply] [d/l] [select] |
|
|
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.
| [reply] [d/l] |
|
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@/
| [reply] [d/l] [select] |
|
Hmm. There seem to be two interacting problems here.
- 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
- 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.
| [reply] [d/l] [select] |
|
|
|
Re: Help me make a test case for Math::BigFloat
by runrig (Abbot) on Mar 06, 2006 at 21:54 UTC
|
#!perl
use bignum;
my $tmp = "-0.00000000000000007573064690121714";
my $e = exp($tmp);
printf("%3.20g\n", $e);
print "$e ", 1-$e, "\n";
| [reply] [d/l] [select] |
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. | [reply] [d/l] [select] |
|
|