Subject: | Forward of RT43460 with patch |
Math::BigFloat seems to have a different set of maintainers though it's included in the Math::BigInt distribution, so I'm making a new ticket here.
https://rt.cpan.org/Ticket/Display.html?id=43460
I'm marking this critical because I keep getting hit by this bug and it produces incorrect results for very normal inputs (see below for some examples).
There has been a fix on that thread for almost a year. Attached is an updated patch. The first change is a performance update for exp, and can be skipped if you do not feel comfortable with it, although it is a few orders of magnitude for large positive or negative values. The second part fixes results of negative and fractional bases in powers and exp.
time perl -Mbignum -E 'say Math::BigFloat->new(-394.84010945715266885,20)->bexp();'
Old:
2112036032039567973928673635099992827419000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
23.100s
New:
0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003335179622786491387299832160510130749927
0.350s
perl -Mbignum=lib,GMP -le 'print 2**(-1034.5)'
Old:
797870447579789479633740082394929951909700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
New:
0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003841222690408590466868250378242558090957
perl -Mbignum -E 'say .01**28.4'
Old:
648531815674.9002481119466764195627749488
New:
0.000000000000000000000000000000000000000000000000000000001584893192461113485202101373391507013269
time perl -Mbignum=lib,GMP -E 'for (1..10000) { $s += exp(8000.01) } say $s'
Old: 418.233s (output elided -- identical for both in this case)
New: 0.470s
Subject: | bigfloat-bpow-fix2.patch |
--- /home/djacobsen/perl5/perlbrew/build/perl-5.21.2/lib/Math/BigFloat.pm 2014-07-18 07:55:23.000000000 -0700
+++ /home/djacobsen/perl5/perlbrew/perls/perl-5.21.2/lib/5.21.2/Math/BigFloat.pm 2014-09-30 16:35:39.639178662 -0700
@@ -1197,8 +1197,22 @@
# $x contains now an estimate of e, with some surplus digits, so we can round
if (!$x_org->is_one())
{
- # raise $x to the wanted power and round it in one step:
- $x->bpow($x_org, @params);
+ # Reduce size of fractional part, followup with integer power of two.
+ my $lshift = 0;
+ while ($lshift < 30 && $x_org->bacmp(2 << $lshift) > 0)
+ {
+ $lshift++;
+ }
+ # Raise $x to the wanted power and round it.
+ if ($lshift == 0)
+ {
+ $x->bpow($x_org, @params);
+ }
+ else
+ {
+ my($mul, $rescale) = (1 << $lshift, $scale+1+$lshift);
+ $x->bpow(scalar $x_org->bdiv($mul,$rescale),$rescale)->bpow($mul, @params);
+ }
}
else
{
@@ -2394,6 +2408,8 @@
my ($limit,$v,$u,$below,$factor,$next,$over);
$u = $x->copy()->blog(undef,$scale)->bmul($y);
+ my $do_invert = ($u->{sign} eq '-');
+ $u->bneg() if $do_invert;
$v = $self->bone(); # 1
$factor = $self->new(2); # 2
$x->bone(); # first term: 1
@@ -2418,6 +2434,12 @@
#$steps++;
}
+
+ if ($do_invert)
+ {
+ my $x_copy = $x->copy;
+ $x->bone->bdiv($x_copy, $scale);
+ }
# shortcut to not run through _find_round_parameters again
if (defined $params[0])