summaryrefslogtreecommitdiff
path: root/lib/Math
diff options
context:
space:
mode:
authorTels <nospam-abuse@bloodgate.com>2004-07-17 18:22:57 +0200
committerRafael Garcia-Suarez <rgarciasuarez@gmail.com>2004-07-19 15:49:12 +0000
commit03874afe4126e47a07c482418278c13f14c14597 (patch)
tree85eff81e754adbd0c6a75b2d67320603abd94d92 /lib/Math
parent689badd5a97f92b96d41abcba9996dc8da00c8a7 (diff)
downloadperl-03874afe4126e47a07c482418278c13f14c14597.tar.gz
[perl #30609] [PATCH] BigInt v1.71 - first try
Message-Id: <200407171622.58443@bloodgate.com> p4raw-id: //depot/perl@23142
Diffstat (limited to 'lib/Math')
-rw-r--r--lib/Math/BigFloat.pm94
-rw-r--r--lib/Math/BigInt.pm9
-rw-r--r--lib/Math/BigInt/Calc.pm105
-rw-r--r--lib/Math/BigInt/t/bare_mbf.t2
-rw-r--r--lib/Math/BigInt/t/bare_mbi.t2
-rw-r--r--lib/Math/BigInt/t/bigfltpm.inc28
-rwxr-xr-xlib/Math/BigInt/t/bigfltpm.t2
-rw-r--r--lib/Math/BigInt/t/bigintpm.inc22
-rwxr-xr-xlib/Math/BigInt/t/bigintpm.t2
-rwxr-xr-xlib/Math/BigInt/t/sub_mbf.t2
-rwxr-xr-xlib/Math/BigInt/t/sub_mbi.t2
-rw-r--r--lib/Math/BigInt/t/with_sub.t2
12 files changed, 144 insertions, 128 deletions
diff --git a/lib/Math/BigFloat.pm b/lib/Math/BigFloat.pm
index f7008aacf6..846f5f03be 100644
--- a/lib/Math/BigFloat.pm
+++ b/lib/Math/BigFloat.pm
@@ -12,14 +12,14 @@ package Math::BigFloat;
# _a : accuracy
# _p : precision
-$VERSION = '1.44';
+$VERSION = '1.45';
require 5.005;
require Exporter;
@ISA = qw(Exporter Math::BigInt);
use strict;
-# $_trap_inf and $_trap_nan are internal and should never be accessed from the outside
+# $_trap_inf/$_trap_nan are internal and should never be accessed from outside
use vars qw/$AUTOLOAD $accuracy $precision $div_scale $round_mode $rnd_mode
$upgrade $downgrade $_trap_nan $_trap_inf/;
my $class = "Math::BigFloat";
@@ -626,30 +626,7 @@ sub badd
$x->bnorm()->round($a,$p,$r,$y);
}
-sub bsub
- {
- # (BigFloat or num_str, BigFloat or num_str) return BigFloat
- # subtract second arg from first, modify first
-
- # set up parameters
- my ($self,$x,$y,$a,$p,$r) = (ref($_[0]),@_);
- # objectify is costly, so avoid it
- if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
- {
- ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
- }
-
- if ($y->is_zero()) # still round for not adding zero
- {
- return $x->round($a,$p,$r);
- }
-
- # $x - $y = -$x + $y
- $y->{sign} =~ tr/+-/-+/; # does nothing for NaN
- $x->badd($y,$a,$p,$r); # badd does not leave internal zeros
- $y->{sign} =~ tr/+-/-+/; # refix $y (does nothing for NaN)
- $x; # already rounded by badd()
- }
+# sub bsub is inherited from Math::BigInt!
sub binc
{
@@ -1293,39 +1270,52 @@ sub bdiv
# enough...
$scale = abs($params[0] || $params[1]) + 4; # take whatever is defined
}
+
+ my $rem; $rem = $self->bzero() if wantarray;
+
+ $y = $self->new($y) unless $y->isa('Math::BigFloat');
+
my $lx = $MBI->_len($x->{_m}); my $ly = $MBI->_len($y->{_m});
$scale = $lx if $lx > $scale;
$scale = $ly if $ly > $scale;
my $diff = $ly - $lx;
$scale += $diff if $diff > 0; # if lx << ly, but not if ly << lx!
-
- # make copy of $x in case of list context for later reminder calculation
- my $rem;
- if (wantarray && !$y->is_one())
+
+ # cases like $x /= $x (but not $x /= $y!) were wrong due to modifying $x
+ # twice below)
+ if (overload::StrVal($x) eq overload::StrVal($y))
{
- $rem = $x->copy();
+ $x->bone(); # x/x => 1, rem 0
}
-
- $x->{sign} = $x->{sign} ne $y->sign() ? '-' : '+';
-
- # check for / +-1 ( +/- 1E0)
- if (!$y->is_one())
+ else
{
- # promote BigInts and it's subclasses (except when already a BigFloat)
- $y = $self->new($y) unless $y->isa('Math::BigFloat');
+
+ # make copy of $x in case of list context for later reminder calculation
+ if (wantarray && !$y->is_one())
+ {
+ $rem = $x->copy();
+ }
- # calculate the result to $scale digits and then round it
- # a * 10 ** b / c * 10 ** d => a/c * 10 ** (b-d)
- $MBI->_lsft($x->{_m},$MBI->_new($scale),10);
- $MBI->_div ($x->{_m},$y->{_m} ); # a/c
+ $x->{sign} = $x->{sign} ne $y->sign() ? '-' : '+';
- ($x->{_e},$x->{_es}) =
- _e_sub($x->{_e}, $y->{_e}, $x->{_es}, $y->{_es});
- # correct for 10**scale
- ($x->{_e},$x->{_es}) =
- _e_sub($x->{_e}, $MBI->_new($scale), $x->{_es}, '+');
- $x->bnorm(); # remove trailing 0's
- }
+ # check for / +-1 ( +/- 1E0)
+ if (!$y->is_one())
+ {
+ # promote BigInts and it's subclasses (except when already a BigFloat)
+ $y = $self->new($y) unless $y->isa('Math::BigFloat');
+
+ # calculate the result to $scale digits and then round it
+ # a * 10 ** b / c * 10 ** d => a/c * 10 ** (b-d)
+ $MBI->_lsft($x->{_m},$MBI->_new($scale),10);
+ $MBI->_div ($x->{_m},$y->{_m}); # a/c
+
+ # correct exponent of $x
+ ($x->{_e},$x->{_es}) = _e_sub($x->{_e}, $y->{_e}, $x->{_es}, $y->{_es});
+ # correct for 10**scale
+ ($x->{_e},$x->{_es}) = _e_sub($x->{_e}, $MBI->_new($scale), $x->{_es}, '+');
+ $x->bnorm(); # remove trailing 0's
+ }
+ } # ende else $x != $y
# shortcut to not run through _find_round_parameters again
if (defined $params[0])
@@ -1343,17 +1333,13 @@ sub bdiv
# clear a/p after round, since user did not request it
delete $x->{_a}; delete $x->{_p};
}
-
+
if (wantarray)
{
if (!$y->is_one())
{
$rem->bmod($y,@params); # copy already done
}
- else
- {
- $rem = $self->bzero();
- }
if ($fallback)
{
# clear a/p after round, since user did not request it
diff --git a/lib/Math/BigInt.pm b/lib/Math/BigInt.pm
index 220920e5e1..af361b41cb 100644
--- a/lib/Math/BigInt.pm
+++ b/lib/Math/BigInt.pm
@@ -18,7 +18,7 @@ package Math::BigInt;
my $class = "Math::BigInt";
require 5.005;
-$VERSION = '1.70_01';
+$VERSION = '1.71';
use Exporter;
@ISA = qw( Exporter );
@EXPORT_OK = qw( objectify bgcd blcm);
@@ -1140,6 +1140,13 @@ sub bsub
return $x;
}
+ if (overload::StrVal($x) eq overload::StrVal($y))
+ {
+ # if we get the same variable twice, the result must be zero (the code
+ # below fails in that case)
+ return $x->bzero(@r) if $x->{sign} =~ /^[+-]$/;
+ return $x->bnan(); # NaN, -inf, +inf
+ }
$y->{sign} =~ tr/+\-/-+/; # does nothing for NaN
$x->badd($y,@r); # badd does not leave internal zeros
$y->{sign} =~ tr/+\-/-+/; # refix $y (does nothing for NaN)
diff --git a/lib/Math/BigInt/Calc.pm b/lib/Math/BigInt/Calc.pm
index f2f0c87466..c90d61b996 100644
--- a/lib/Math/BigInt/Calc.pm
+++ b/lib/Math/BigInt/Calc.pm
@@ -6,7 +6,7 @@ use strict;
use vars qw/$VERSION/;
-$VERSION = '0.40';
+$VERSION = '0.41';
# Package to store unsigned big integers in decimal and do math with them
@@ -97,6 +97,21 @@ sub _base_len
return ($BASE_LEN, $AND_BITS, $XOR_BITS, $OR_BITS, $BASE_LEN_SMALL, $MAX_VAL);
}
+sub _new
+ {
+ # (ref to string) return ref to num_array
+ # Convert a number from string format (without sign) to internal base
+ # 1ex format. Assumes normalized value as input.
+ my $il = length($_[1])-1;
+
+ # < BASE_LEN due len-1 above
+ return [ int($_[1]) ] if $il < $BASE_LEN; # shortcut for short numbers
+
+ # this leaves '00000' instead of int 0 and will be corrected after any op
+ [ reverse(unpack("a" . ($il % $BASE_LEN+1)
+ . ("a$BASE_LEN" x ($il / $BASE_LEN)), $_[1])) ];
+ }
+
BEGIN
{
# from Daniel Pfeiffer: determine largest group of digits that is precisely
@@ -123,28 +138,7 @@ BEGIN
use integer;
- ############################################################################
- # the next block is no longer important
-
- ## this below detects 15 on a 64 bit system, because after that it becomes
- ## 1e16 and not 1000000 :/ I can make it detect 18, but then I get a lot of
- ## test failures. Ugh! (Tomake detect 18: uncomment lines marked with *)
-
- #my $bi = 5; # approx. 16 bit
- #$num = int('9' x $bi);
- ## $num = 99999; # *
- ## while ( ($num+$num+1) eq '1' . '9' x $bi) # *
- #while ( int($num+$num+1) eq '1' . '9' x $bi)
- # {
- # $bi++; $num = int('9' x $bi);
- # # $bi++; $num *= 10; $num += 9; # *
- # }
- #$bi--; # back off one step
- # by setting them equal, we ignore the findings and use the default
- # one-size-fits-all approach from former versions
- my $bi = $e; # XXX, this should work always
-
- __PACKAGE__->_base_len($e,$bi); # set and store
+ __PACKAGE__->_base_len($e); # set and store
# find out how many bits _and, _or and _xor can take (old default = 16)
# I don't think anybody has yet 128 bit scalars, so let's play safe.
@@ -179,32 +173,13 @@ BEGIN
} while ($OR_BITS < $max && $x == $z && $y == $x);
$OR_BITS --; # retreat one step
- }
-
-###############################################################################
-
-sub _new
- {
- # (ref to string) return ref to num_array
- # Convert a number from string format (without sign) to internal base
- # 1ex format. Assumes normalized value as input.
- my $il = length($_[1])-1;
-
- # < BASE_LEN due len-1 above
- return [ int($_[1]) ] if $il < $BASE_LEN; # shortcut for short numbers
-
- # this leaves '00000' instead of int 0 and will be corrected after any op
- [ reverse(unpack("a" . ($il % $BASE_LEN+1)
- . ("a$BASE_LEN" x ($il / $BASE_LEN)), $_[1])) ];
- }
-
-BEGIN
- {
$AND_MASK = __PACKAGE__->_new( ( 2 ** $AND_BITS ));
$XOR_MASK = __PACKAGE__->_new( ( 2 ** $XOR_BITS ));
$OR_MASK = __PACKAGE__->_new( ( 2 ** $OR_BITS ));
}
+###############################################################################
+
sub _zero
{
# create a zero
@@ -968,7 +943,7 @@ sub _digit
my $elem = int($n / $BASE_LEN); # which array element
my $digit = $n % $BASE_LEN; # which digit in this element
- $elem = '0000'.@$x[$elem]; # get element padded with 0's
+ $elem = '0000000'.@$x[$elem]; # get element padded with 0's
substr($elem,-$digit-1,1);
}
@@ -1761,11 +1736,7 @@ sub _as_hex
my ($c,$x) = @_;
# fit's into one element (handle also 0x0 case)
- if (@$x == 1)
- {
- my $t = sprintf("0x%x",$x->[0]);
- return $t;
- }
+ return sprintf("0x%x",$x->[0]) if @$x == 1;
my $x1 = _copy($c,$x);
@@ -1779,7 +1750,6 @@ sub _as_hex
{
$x10000 = [ 0x1000 ]; $h = 'h3';
}
- # while (! _is_zero($c,$x1))
while (@$x1 != 1 || $x1->[0] != 0) # _is_zero()
{
($x1, $xr) = _div($c,$x1,$x10000);
@@ -1787,8 +1757,7 @@ sub _as_hex
}
$es = reverse $es;
$es =~ s/^[0]+//; # strip leading zeros
- $es = '0x' . $es;
- $es;
+ '0x' . $es; # return result prepended with 0x
}
sub _as_bin
@@ -1819,7 +1788,6 @@ sub _as_bin
{
$x10000 = [ 0x1000 ]; $b = 'b12';
}
- # while (! _is_zero($c,$x1))
while (!(@$x1 == 1 && $x1->[0] == 0)) # _is_zero()
{
($x1, $xr) = _div($c,$x1,$x10000);
@@ -1828,8 +1796,7 @@ sub _as_bin
}
$es = reverse $es;
$es =~ s/^[0]+//; # strip leading zeros
- $es = '0b' . $es;
- $es;
+ '0b' . $es; # return result prepended with 0b
}
sub _from_hex
@@ -1837,19 +1804,26 @@ sub _from_hex
# 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 $d = 7; # 7 digits at a time
+ if ($] <= 5.006)
+ {
+ # for older Perls, play safe
+ $m = [ 0x10000 ]; # 16 bit at a time (<32 bit!)
+ $d = 4; # 4 digits at a time
+ }
+
my $mul = _one();
- my $m = [ 0x10000 ]; # 16 bit at a time
my $x = _zero();
- my $len = length($hs)-2;
- $len = int($len/4); # 4-digit parts, w/o '0x'
- my $val; my $i = -4;
+ my $len = int( (length($hs)-2)/$d ); # $d digit parts, w/o the '0x'
+ my $val; my $i = -$d;
while ($len >= 0)
{
- $val = substr($hs,$i,4);
+ $val = substr($hs,$i,$d); # get hex digits
$val =~ s/^[+-]?0x// if $len == 0; # for last part only because
$val = hex($val); # hex does not like wrong chars
- $i -= 4; $len --;
+ $i -= $d; $len --;
_add ($c, $x, _mul ($c, [ $val ], $mul ) ) if $val != 0;
_mul ($c, $mul, $m ) if $len >= 0; # skip last mul
}
@@ -1868,9 +1842,9 @@ sub _from_bin
$hs =~ s/^[+-]?0b//; # remove sign and 0b
my $l = length($hs); # bits
$hs = '0' x (8-($l % 8)) . $hs if ($l % 8) != 0; # padd left side w/ 0
- my $h = unpack('H*', pack ('B*', $hs)); # repack as hex
+ my $h = '0x' . unpack('H*', pack ('B*', $hs)); # repack as hex
- $c->_from_hex('0x'.$h);
+ $c->_from_hex($h);
}
##############################################################################
@@ -1903,8 +1877,7 @@ sub _modinv
# if the gcd is not 1, then return NaN
return (undef,undef) unless _is_one($c,$a);
- $sign = $sign == 1 ? '+' : '-';
- ($u1,$sign);
+ ($u1, $sign == 1 ? '+' : '-');
}
sub _modpow
diff --git a/lib/Math/BigInt/t/bare_mbf.t b/lib/Math/BigInt/t/bare_mbf.t
index cbca372454..336ca01679 100644
--- a/lib/Math/BigInt/t/bare_mbf.t
+++ b/lib/Math/BigInt/t/bare_mbf.t
@@ -27,7 +27,7 @@ BEGIN
}
print "# INC = @INC\n";
- plan tests => 1815;
+ plan tests => 1835;
}
use Math::BigFloat lib => 'BareCalc';
diff --git a/lib/Math/BigInt/t/bare_mbi.t b/lib/Math/BigInt/t/bare_mbi.t
index 6514e1ec43..4f8b0ae126 100644
--- a/lib/Math/BigInt/t/bare_mbi.t
+++ b/lib/Math/BigInt/t/bare_mbi.t
@@ -26,7 +26,7 @@ BEGIN
}
print "# INC = @INC\n";
- plan tests => 2832;
+ plan tests => 2848;
}
use Math::BigInt lib => 'BareCalc';
diff --git a/lib/Math/BigInt/t/bigfltpm.inc b/lib/Math/BigInt/t/bigfltpm.inc
index 5e1c19f921..4e38e5b127 100644
--- a/lib/Math/BigInt/t/bigfltpm.inc
+++ b/lib/Math/BigInt/t/bigfltpm.inc
@@ -257,6 +257,34 @@ ok ($class->new(-1)->is_one('-'),1);
ok ($class->new(1)->fdiv('0.5')->bsstr(),'2e+0');
+###############################################################################
+# [perl #30609] bug with $x -= $x not beeing 0, but 2*$x
+
+$x = $class->new(3); $x -= $x; ok ($x, 0);
+$x = $class->new(-3); $x -= $x; ok ($x, 0);
+$x = $class->new(3); $x += $x; ok ($x, 6);
+$x = $class->new(-3); $x += $x; ok ($x, -6);
+
+$x = $class->new('NaN'); $x -= $x; ok ($x->is_nan(), 1);
+$x = $class->new('inf'); $x -= $x; ok ($x->is_nan(), 1);
+$x = $class->new('-inf'); $x -= $x; ok ($x->is_nan(), 1);
+
+$x = $class->new('NaN'); $x += $x; ok ($x->is_nan(), 1);
+$x = $class->new('inf'); $x += $x; ok ($x->is_inf(), 1);
+$x = $class->new('-inf'); $x += $x; ok ($x->is_inf('-'), 1);
+
+$x = $class->new('3.14'); $x -= $x; ok ($x, 0);
+$x = $class->new('-3.14'); $x -= $x; ok ($x, 0);
+$x = $class->new('3.14'); $x += $x; ok ($x, '6.28');
+$x = $class->new('-3.14'); $x += $x; ok ($x, '-6.28');
+
+$x = $class->new('3.14'); $x *= $x; ok ($x, '9.8596');
+$x = $class->new('-3.14'); $x *= $x; ok ($x, '9.8596');
+$x = $class->new('3.14'); $x /= $x; ok ($x, '1');
+$x = $class->new('-3.14'); $x /= $x; ok ($x, '1');
+$x = $class->new('3.14'); $x %= $x; ok ($x, '0');
+$x = $class->new('-3.14'); $x %= $x; ok ($x, '0');
+
1; # all done
###############################################################################
diff --git a/lib/Math/BigInt/t/bigfltpm.t b/lib/Math/BigInt/t/bigfltpm.t
index 9e50f5e7c4..b81114c330 100755
--- a/lib/Math/BigInt/t/bigfltpm.t
+++ b/lib/Math/BigInt/t/bigfltpm.t
@@ -26,7 +26,7 @@ BEGIN
}
print "# INC = @INC\n";
- plan tests => 1815
+ plan tests => 1835
+ 2; # own tests
}
diff --git a/lib/Math/BigInt/t/bigintpm.inc b/lib/Math/BigInt/t/bigintpm.inc
index cdefea633a..77b55b9b98 100644
--- a/lib/Math/BigInt/t/bigintpm.inc
+++ b/lib/Math/BigInt/t/bigintpm.inc
@@ -624,6 +624,28 @@ ok ($class->new(1)->is_one(),1);
ok ($class->new(-1)->is_one(),0);
###############################################################################
+# [perl #30609] bug with $x -= $x not beeing 0, but 2*$x
+
+$x = $class->new(3); $x -= $x; ok ($x, 0);
+$x = $class->new(-3); $x -= $x; ok ($x, 0);
+$x = $class->new('NaN'); $x -= $x; ok ($x->is_nan(), 1);
+$x = $class->new('inf'); $x -= $x; ok ($x->is_nan(), 1);
+$x = $class->new('-inf'); $x -= $x; ok ($x->is_nan(), 1);
+
+$x = $class->new('NaN'); $x += $x; ok ($x->is_nan(), 1);
+$x = $class->new('inf'); $x += $x; ok ($x->is_inf(), 1);
+$x = $class->new('-inf'); $x += $x; ok ($x->is_inf('-'), 1);
+$x = $class->new(3); $x += $x; ok ($x, 6);
+$x = $class->new(-3); $x += $x; ok ($x, -6);
+
+$x = $class->new(3); $x *= $x; ok ($x, 9);
+$x = $class->new(-3); $x *= $x; ok ($x, 9);
+$x = $class->new(3); $x /= $x; ok ($x, 1);
+$x = $class->new(-3); $x /= $x; ok ($x, 1);
+$x = $class->new(3); $x %= $x; ok ($x, 0);
+$x = $class->new(-3); $x %= $x; ok ($x, 0);
+
+###############################################################################
# all tests done
1;
diff --git a/lib/Math/BigInt/t/bigintpm.t b/lib/Math/BigInt/t/bigintpm.t
index 50fca1dbf1..ba0b31495b 100755
--- a/lib/Math/BigInt/t/bigintpm.t
+++ b/lib/Math/BigInt/t/bigintpm.t
@@ -10,7 +10,7 @@ BEGIN
my $location = $0; $location =~ s/bigintpm.t//;
unshift @INC, $location; # to locate the testing files
chdir 't' if -d 't';
- plan tests => 2832;
+ plan tests => 2848;
}
use Math::BigInt;
diff --git a/lib/Math/BigInt/t/sub_mbf.t b/lib/Math/BigInt/t/sub_mbf.t
index 8550a97ded..e72506c631 100755
--- a/lib/Math/BigInt/t/sub_mbf.t
+++ b/lib/Math/BigInt/t/sub_mbf.t
@@ -26,7 +26,7 @@ BEGIN
}
print "# INC = @INC\n";
- plan tests => 1815
+ plan tests => 1835
+ 6; # + our own tests
}
diff --git a/lib/Math/BigInt/t/sub_mbi.t b/lib/Math/BigInt/t/sub_mbi.t
index 3e831c5fe4..69abaae17d 100755
--- a/lib/Math/BigInt/t/sub_mbi.t
+++ b/lib/Math/BigInt/t/sub_mbi.t
@@ -26,7 +26,7 @@ BEGIN
}
print "# INC = @INC\n";
- plan tests => 2832
+ plan tests => 2848
+ 5; # +5 own tests
}
diff --git a/lib/Math/BigInt/t/with_sub.t b/lib/Math/BigInt/t/with_sub.t
index 3d480304a9..be6efa042b 100644
--- a/lib/Math/BigInt/t/with_sub.t
+++ b/lib/Math/BigInt/t/with_sub.t
@@ -28,7 +28,7 @@ BEGIN
}
print "# INC = @INC\n";
- plan tests => 1815
+ plan tests => 1835
+ 1;
}