Moin,
attached is a patch to sync blead to the upcoming BigInt v1.73. I would like
to get it in to get some testing exposure. Here is a summary on what changed:
* Mark Lakata found a bug in from_hex(): seems it was constructing numbers
with one element, even if the number didn't fit into one element. Thus
corruped objects resulted. That bug was introduced in v1.72.
* <<= and =>> are now overloaded, thus making these operations about 20%
faster (for smaller numbers etc) by not making a copy.
* lot's of inf/Nan finxes for bpow() - Thanx to Hugo and a very old bug report
on rt.cpan.org (sorry that I didn't see it earlier)
* BigFloat->new() no longer does _zeros() - this helps libs like GMP that
store numbers in hex, because _zeros() needs a O(N^2) conversion back to
decimal to look how many trailing zeros we have. Saves roughly 50% for large
numbers
* BigFloat->new() saves on _is_zero() call
* BigFloat->new() has a shortcut for simple forms of numbers, saving a lot of
work on them (like +123, -129 etc)
* Lotsa tests to excercie the new stuff and bugfix
Best wishes,
Tels
--
Note: Due to a system crash I am currently not
able to sign my messages with my PGP key.
diff -ruN blead-perl/lib/Math/BigFloat.pm blead-perl-patch/lib/Math/BigFloat.pm
--- blead-perl/lib/Math/BigFloat.pm 2004-08-13 15:43:48.000000000 +0200
+++ blead-perl-patch/lib/Math/BigFloat.pm 2004-10-10 21:31:16.000000000 +0200
@@ -12,7 +12,7 @@
# _a : accuracy
# _p : precision
-$VERSION = '1.46';
+$VERSION = '1.47';
require 5.005;
require Exporter;
@@ -132,7 +132,8 @@
$self->{sign} = $wanted->sign();
return $self->bnorm();
}
- # got string
+ # else: got a string
+
# handle '+inf', '-inf' first
if ($wanted =~ /^[+-]?inf$/)
{
@@ -146,6 +147,17 @@
return $self->bnorm();
}
+ # shortcut for simple forms like '12' that neither have trailing nor leading
+ # zeros
+ if ($wanted =~ /^([+-]?)([1-9][0-9]*[1-9])$/)
+ {
+ $self->{_e} = $MBI->_zero();
+ $self->{_es} = '+';
+ $self->{sign} = $1 || '+';
+ $self->{_m} = $MBI->_new($2);
+ return $self->round(@r) if !$downgrade;
+ }
+
my ($mis,$miv,$mfv,$es,$ev) = Math::BigInt::_split($wanted);
if (!ref $mis)
{
@@ -178,22 +190,28 @@
($self->{_e}, $self->{_es}) =
_e_sub ($self->{_e}, $len, $self->{_es}, '+');
}
- $self->{sign} = $$mis;
-
- # we can only have trailing zeros on the mantissa of $$mfv eq ''
- if (CORE::length($$mfv) == 0)
+ # we can only have trailing zeros on the mantissa if $$mfv eq ''
+ else
{
- my $zeros = $MBI->_zeros($self->{_m}); # correct for trailing zeros
+ # Use a regexp to count the trailing zeros in $$miv instead of _zeros()
+ # because that is faster, especially when _m is not stored in base 10.
+ my $zeros = 0; $zeros = CORE::length($1) if $$miv =~ /[1-9](0*)$/;
if ($zeros != 0)
{
my $z = $MBI->_new($zeros);
+ # turn '120e2' into '12e3'
$MBI->_rsft ( $self->{_m}, $z, 10);
_e_add ( $self->{_e}, $z, $self->{_es}, '+');
}
}
+ $self->{sign} = $$mis;
+
# for something like 0Ey, set y to 1, and -0 => +0
+ # Check $$miv for beeing '0' and $$mfv eq '', because otherwise _m could not
+ # have become 0. That's faster than to call $MBI->_is_zero().
$self->{sign} = '+', $self->{_e} = $MBI->_one()
- if $MBI->_is_zero($self->{_m});
+ if $$miv eq '0' and $$mfv eq '';
+
return $self->round(@r) if !$downgrade;
}
# if downgrade, inf, NaN or integers go down
@@ -1887,8 +1905,11 @@
($self,$x,$y,$a,$p,$r) = objectify(2,@_);
}
- return $x if $x->{sign} =~ /^[+-]inf$/;
return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan;
+ return $x if $x->{sign} =~ /^[+-]inf$/;
+
+ # -2 ** -2 => NaN
+ return $x->bnan() if $x->{sign} eq '-' && $y->{sign} eq '-';
# cache the result of is_zero
my $y_is_zero = $y->is_zero();
@@ -1896,7 +1917,7 @@
return $x if $x->is_one() || $y->is_one();
my $x_is_zero = $x->is_zero();
- return $x->_pow($y,$a,$p,$r) if !$x_is_zero && !$y->is_int(); # non-integer
power
+ return $x->_pow($y,$a,$p,$r) if !$x_is_zero && !$y->is_int(); #
non-integer power
my $y1 = $y->as_number()->{value}; # make MBI part
diff -ruN blead-perl/lib/Math/BigInt/Calc.pm blead-perl-patch/lib/Math/BigInt/Calc.pm
--- blead-perl/lib/Math/BigInt/Calc.pm 2004-08-13 15:43:48.000000000 +0200
+++ blead-perl-patch/lib/Math/BigInt/Calc.pm 2004-10-10 22:31:18.000000000 +0200
@@ -6,7 +6,7 @@
use vars qw/$VERSION/;
-$VERSION = '0.42';
+$VERSION = '0.43';
# Package to store unsigned big integers in decimal and do math with them
@@ -37,7 +37,7 @@
# constants for easier life
my $nan = 'NaN';
-my ($MBASE,$BASE,$RBASE,$BASE_LEN,$MAX_VAL,$BASE_LEN2,$BASE_LEN_SMALL);
+my ($MBASE,$BASE,$RBASE,$BASE_LEN,$MAX_VAL,$BASE_LEN_SMALL);
my ($AND_BITS,$XOR_BITS,$OR_BITS);
my ($AND_MASK,$XOR_MASK,$OR_MASK);
@@ -68,7 +68,6 @@
$BASE_LEN = shift if (defined $_[0]); # one more arg?
$BASE = int("1e".$BASE_LEN);
- $BASE_LEN2 = int($BASE_LEN_SMALL / 2); # for mul shortcut
$MBASE = int("1e".$BASE_LEN_SMALL);
$RBASE = abs('1e-'.$BASE_LEN_SMALL); # see USE_MUL
$MAX_VAL = $MBASE-1;
@@ -1804,7 +1803,7 @@
# convert a hex number to decimal (ref to string, return ref to array)
my ($c,$hs) = @_;
- my $m = [ 0x10000000 ]; # 28 bit at a time (<32 bit!)
+ my $m = _new($c, 0x10000000); # 28 bit at a time (<32 bit!)
my $d = 7; # 7 digits at a time
if ($] <= 5.006)
{
@@ -1824,7 +1823,14 @@
$val =~ s/^[+-]?0x// if $len == 0; # for last part only because
$val = hex($val); # hex does not like wrong chars
$i -= $d; $len --;
- _add ($c, $x, _mul ($c, [ $val ], $mul ) ) if $val != 0;
+ my $adder = [ $val ];
+ # if the resulting number was to big to fit into one element, create a
+ # two-element version (bug found by Mark Lakata - Thanx!)
+ if (CORE::length($val) > $BASE_LEN)
+ {
+ $adder = _new($c,$val);
+ }
+ _add ($c, $x, _mul ($c, $adder, $mul ) ) if $val != 0;
_mul ($c, $mul, $m ) if $len >= 0; # skip last mul
}
$x;
diff -ruN blead-perl/lib/Math/BigInt/t/bare_mbf.t
blead-perl-patch/lib/Math/BigInt/t/bare_mbf.t
--- blead-perl/lib/Math/BigInt/t/bare_mbf.t 2004-08-13 15:43:48.000000000 +0200
+++ blead-perl-patch/lib/Math/BigInt/t/bare_mbf.t 2004-10-10 21:51:54.000000000
+0200
@@ -27,7 +27,7 @@
}
print "# INC = @INC\n";
- plan tests => 1861;
+ plan tests => 1924;
}
use Math::BigFloat lib => 'BareCalc';
diff -ruN blead-perl/lib/Math/BigInt/t/bare_mbi.t
blead-perl-patch/lib/Math/BigInt/t/bare_mbi.t
--- blead-perl/lib/Math/BigInt/t/bare_mbi.t 2004-07-19 18:11:52.000000000 +0200
+++ blead-perl-patch/lib/Math/BigInt/t/bare_mbi.t 2004-10-10 21:50:32.000000000
+0200
@@ -26,7 +26,7 @@
}
print "# INC = @INC\n";
- plan tests => 2848;
+ plan tests => 2952;
}
use Math::BigInt lib => 'BareCalc';
diff -ruN blead-perl/lib/Math/BigInt/t/bigfltpm.inc
blead-perl-patch/lib/Math/BigInt/t/bigfltpm.inc
--- blead-perl/lib/Math/BigInt/t/bigfltpm.inc 2004-08-13 15:43:48.000000000 +0200
+++ blead-perl-patch/lib/Math/BigInt/t/bigfltpm.inc 2004-10-10 21:49:19.000000000
+0200
@@ -300,6 +300,29 @@
$x = $class->new(".222222222222222222222222222222222222222222");
ok ($x->bceil(), 1, 'no warnings and one as result');
+###############################################################################
+# test **=, <<=, >>=
+
+# ((2^148)-1)/17
+$x = $class->new(2); $x **= 148; $x++; $x->bdiv(17, 60)->bfloor();
$x->accuracy(undef);
+ok ($x,"20988936657440586486151264256610222593863921");
+ok ($x->length(),length "20988936657440586486151264256610222593863921");
+
+$x = $class->new('2');
+my $y = $class->new('18');
+ok ($x <<= $y, 2 << 18);
+ok ($x, 2 << 18);
+ok ($x >>= $y, 2);
+ok ($x, 2);
+
+$x = $class->new('2');
+$y = $class->new('18.2');
+$x <<= $y; # 2 * (2 ** 18.2);
+
+ok ($x->copy()->bfround(-9), '602248.763144685');
+ok ($x >>= $y, 2); # 2 * (2 ** 18.2) / (2 ** 18.2) => 2
+ok ($x, 2);
+
1; # all done
###############################################################################
@@ -373,10 +396,37 @@
1__2:NaN
1E1__2:NaN
11__2E2:NaN
-#1.E3:NaN
.2E-3.:NaN
-#1e3e4:NaN
+1e3e4:NaN
+# strange, but valid
.2E2:20
+1.E3:1000
+# some inputs that result in zero
+0e0:0
++0e0:0
++0e+0:0
+-0e+0:0
+0e-0:0
+-0e-0:0
++0e-0:0
+000:0
+00e2:0
+00e02:0
+000e002:0
+000e1230:0
+00e-3:0
+00e+3:0
+00e-03:0
+00e+03:0
+-000:0
+-00e2:0
+-00e02:0
+-000e002:0
+-000e1230:0
+-00e-3:0
+-00e+3:0
+-00e-03:0
+-00e+03:0
&as_number
0:0
1:1
diff -ruN blead-perl/lib/Math/BigInt/t/bigfltpm.t
blead-perl-patch/lib/Math/BigInt/t/bigfltpm.t
--- blead-perl/lib/Math/BigInt/t/bigfltpm.t 2004-08-13 15:43:48.000000000 +0200
+++ blead-perl-patch/lib/Math/BigInt/t/bigfltpm.t 2004-10-10 21:52:07.000000000
+0200
@@ -26,7 +26,7 @@
}
print "# INC = @INC\n";
- plan tests => 1861
+ plan tests => 1924
+ 2; # own tests
}
diff -ruN blead-perl/lib/Math/BigInt/t/bigintpm.inc
blead-perl-patch/lib/Math/BigInt/t/bigintpm.inc
--- blead-perl/lib/Math/BigInt/t/bigintpm.inc 2004-07-19 18:11:52.000000000 +0200
+++ blead-perl-patch/lib/Math/BigInt/t/bigintpm.inc 2004-10-10 22:32:15.000000000
+0200
@@ -476,6 +476,14 @@
($x,$y) = $x->length();
ok ($x,13); ok ($y,0);
+# test <<=, >>=
+$x = $class->new('2');
+my $y = $class->new('18');
+ok ($x <<= $y, 2 << 18);
+ok ($x, 2 << 18);
+ok ($x >>= $y, 2);
+ok ($x, 2);
+
# I am afraid the following is not yet possible due to slowness
# Also, testing for 2 meg output is a bit hard ;)
#$x = $class->new(2); $x **= 6972593; $x--;
@@ -936,6 +944,8 @@
0x200000001:8589934593
0x400000001:17179869185
0x800000001:34359738369
+# bug found by Mark Lakata in Calc.pm creating too big one-element numbers in
_from_hex()
+0x2dd59e18a125dbed30a6ab1d93e9c855569f44f75806f0645dc9a2e98b808c3:1295719234436071846486578237372801883390756472611551858964079371952886122691
# inf input
inf:inf
+inf:inf
@@ -999,6 +1009,32 @@
012345678912:12345678912
0123456789123:123456789123
01234567891234:1234567891234
+# some inputs that result in zero
+0e0:0
++0e0:0
++0e+0:0
+-0e+0:0
+0e-0:0
+-0e-0:0
++0e-0:0
+000:0
+00e2:0
+00e02:0
+000e002:0
+000e1230:0
+00e-3:0
+00e+3:0
+00e-03:0
+00e+03:0
+-000:0
+-00e2:0
+-00e02:0
+-000e002:0
+-000e1230:0
+-00e-3:0
+-00e+3:0
+-00e-03:0
+-00e+03:0
# normal input
0:0
+0:0
@@ -1976,14 +2012,40 @@
2:2:4
2:3:8
3:3:27
+-2:2:4
+-2:3:-8
+-2:4:16
+-2:5:-32
2:-1:NaN
-2:-1:NaN
2:-2:NaN
-2:-2:NaN
+# inf tests
+inf:1234500012:inf
--inf:1234500012:-inf
+-inf:1234500012:inf
+-inf:1234500013:-inf
+inf:-12345000123:inf
-inf:-12345000123:-inf
+# -inf * -inf = inf
+-inf:2:inf
+-inf:0:NaN
+-inf:-1:0
+-inf:inf:NaN
+2:inf:inf
+2:-inf:0
+0:inf:0
+0:-inf:inf
+-1:-inf:NaN
+-1:inf:NaN
+-2:inf:NaN
+-2:-inf:0
+NaN:inf:NaN
+NaN:-inf:NaN
+-inf:NaN:NaN
+inf:NaN:NaN
+inf:-inf:NaN
+1:inf:1
+1:-inf:1
# 1 ** -x => 1 / (1 ** x)
-1:0:1
-2:0:1
diff -ruN blead-perl/lib/Math/BigInt/t/bigintpm.t
blead-perl-patch/lib/Math/BigInt/t/bigintpm.t
--- blead-perl/lib/Math/BigInt/t/bigintpm.t 2004-07-19 18:11:52.000000000 +0200
+++ blead-perl-patch/lib/Math/BigInt/t/bigintpm.t 2004-10-10 21:50:17.000000000
+0200
@@ -10,7 +10,7 @@
my $location = $0; $location =~ s/bigintpm.t//;
unshift @INC, $location; # to locate the testing files
chdir 't' if -d 't';
- plan tests => 2848;
+ plan tests => 2952;
}
use Math::BigInt;
diff -ruN blead-perl/lib/Math/BigInt/t/sub_mbf.t
blead-perl-patch/lib/Math/BigInt/t/sub_mbf.t
--- blead-perl/lib/Math/BigInt/t/sub_mbf.t 2004-08-13 15:43:48.000000000 +0200
+++ blead-perl-patch/lib/Math/BigInt/t/sub_mbf.t 2004-10-10 21:52:50.000000000
+0200
@@ -26,7 +26,7 @@
}
print "# INC = @INC\n";
- plan tests => 1861
+ plan tests => 1924
+ 6; # + our own tests
}
diff -ruN blead-perl/lib/Math/BigInt/t/sub_mbi.t
blead-perl-patch/lib/Math/BigInt/t/sub_mbi.t
--- blead-perl/lib/Math/BigInt/t/sub_mbi.t 2004-07-19 18:11:52.000000000 +0200
+++ blead-perl-patch/lib/Math/BigInt/t/sub_mbi.t 2004-10-10 21:50:47.000000000
+0200
@@ -26,7 +26,7 @@
}
print "# INC = @INC\n";
- plan tests => 2848
+ plan tests => 2952
+ 5; # +5 own tests
}
diff -ruN blead-perl/lib/Math/BigInt/t/upgrade.inc
blead-perl-patch/lib/Math/BigInt/t/upgrade.inc
--- blead-perl/lib/Math/BigInt/t/upgrade.inc 2004-02-19 22:39:28.000000000 +0100
+++ blead-perl-patch/lib/Math/BigInt/t/upgrade.inc 2004-09-26 13:06:29.000000000
+0200
@@ -1282,7 +1282,8 @@
2:-2:NaN
-2:-2:NaN
+inf:1234500012:inf
--inf:1234500012:-inf
+-inf:1234500012:inf
+-inf:1234500013:-inf
+inf:-12345000123:inf
-inf:-12345000123:-inf
# 1 ** -x => 1 / (1 ** x)
diff -ruN blead-perl/lib/Math/BigInt/t/upgrade.t
blead-perl-patch/lib/Math/BigInt/t/upgrade.t
--- blead-perl/lib/Math/BigInt/t/upgrade.t 2004-02-19 22:39:28.000000000 +0100
+++ blead-perl-patch/lib/Math/BigInt/t/upgrade.t 2004-09-26 13:06:41.000000000
+0200
@@ -26,7 +26,7 @@
}
print "# INC = @INC\n";
- plan tests => 2098
+ plan tests => 2100
+ 2; # our own tests
}
diff -ruN blead-perl/lib/Math/BigInt/t/with_sub.t
blead-perl-patch/lib/Math/BigInt/t/with_sub.t
--- blead-perl/lib/Math/BigInt/t/with_sub.t 2004-08-13 15:43:48.000000000 +0200
+++ blead-perl-patch/lib/Math/BigInt/t/with_sub.t 2004-10-10 21:51:59.000000000
+0200
@@ -28,7 +28,7 @@
}
print "# INC = @INC\n";
- plan tests => 1861
+ plan tests => 1924
+ 1;
}
diff -ruN blead-perl/lib/Math/BigInt.pm blead-perl-patch/lib/Math/BigInt.pm
--- blead-perl/lib/Math/BigInt.pm 2004-08-13 15:43:48.000000000 +0200
+++ blead-perl-patch/lib/Math/BigInt.pm 2004-09-26 12:57:02.000000000 +0200
@@ -18,7 +18,7 @@
my $class = "Math::BigInt";
require 5.005;
-$VERSION = '1.72';
+$VERSION = '1.73';
use Exporter;
@ISA = qw( Exporter );
@EXPORT_OK = qw( objectify bgcd blcm);
@@ -55,6 +55,9 @@
'|=' => sub { $_[0]->bior($_[1]); },
'**=' => sub { $_[0]->bpow($_[1]); },
+'<<=' => sub { $_[0]->blsft($_[1]); },
+'>>=' => sub { $_[0]->brsft($_[1]); },
+
# not supported by Perl yet
'..' => \&_pointpoint,
@@ -79,7 +82,7 @@
'sqrt' => sub { $_[0]->copy()->bsqrt(); },
'~' => sub { $_[0]->copy()->bnot(); },
-# for sub it is a bit tricky to keep b: b-a => -a+b
+# for subtract it is a bit tricky to keep b: b-a => -a+b
'-' => sub { my $c = $_[0]->copy; $_[2] ?
$c->bneg()->badd($_[1]) :
$c->bsub( $_[1]) },
@@ -1670,12 +1673,61 @@
return $x if $x->modify('bpow');
+ return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan;
+
+ # inf handling
+ if (($x->{sign} =~ /^[+-]inf$/) || ($y->{sign} =~ /^[+-]inf$/))
+ {
+ if (($x->{sign} =~ /^[+-]inf$/) && ($y->{sign} =~ /^[+-]inf$/))
+ {
+ # +-inf ** +-inf
+ return $x->bnan();
+ }
+ # +-inf ** Y
+ if ($x->{sign} =~ /^[+-]inf/)
+ {
+ # +inf ** 0 => NaN
+ return $x->bnan() if $y->is_zero();
+ # -inf ** -1 => 1/inf => 0
+ return $x->bzero() if $y->is_one('-') && $x->is_negative();
+
+ # +inf ** Y => inf
+ return $x if $x->{sign} eq '+inf';
+
+ # -inf ** Y => -inf if Y is odd
+ return $x if $y->is_odd();
+ return $x->babs();
+ }
+ # X ** +-inf
+
+ # 1 ** +inf => 1
+ return $x if $x->is_one();
+
+ # 0 ** inf => 0
+ return $x if $x->is_zero() && $y->{sign} =~ /^[+]/;
+
+ # 0 ** -inf => inf
+ return $x->binf() if $x->is_zero();
+
+ # -1 ** -inf => NaN
+ return $x->bnan() if $x->is_one('-') && $y->{sign} =~ /^[-]/;
+
+ # -X ** -inf => 0
+ return $x->bzero() if $x->{sign} eq '-' && $y->{sign} =~ /^[-]/;
+
+ # -1 ** inf => NaN
+ return $x->bnan() if $x->{sign} eq '-';
+
+ # X ** inf => inf
+ return $x->binf() if $y->{sign} =~ /^[+]/;
+ # X ** -inf => 0
+ return $x->bzero();
+ }
+
return $upgrade->bpow($upgrade->new($x),$y,@r)
if defined $upgrade && !$y->isa($self);
$r[3] = $y; # no push!
- return $x if $x->{sign} =~ /^[+-]inf$/; # -inf/+inf ** x
- return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan;
# cases 0 ** Y, X ** 0, X ** 1, 1 ** Y are handled by Calc or Emu