Patch: BigInt v1.73 (pre-release)
Tels [Sun, 10 Oct 2004 22:36:03 +0000 (00:36 +0200)]
Message-Id: <200410102236.03637@bloodgate.com>

p4raw-id: //depot/perl@23359

14 files changed:
lib/Math/BigFloat.pm
lib/Math/BigInt.pm
lib/Math/BigInt/Calc.pm
lib/Math/BigInt/t/bare_mbf.t
lib/Math/BigInt/t/bare_mbi.t
lib/Math/BigInt/t/bigfltpm.inc
lib/Math/BigInt/t/bigfltpm.t
lib/Math/BigInt/t/bigintpm.inc
lib/Math/BigInt/t/bigintpm.t
lib/Math/BigInt/t/sub_mbf.t
lib/Math/BigInt/t/sub_mbi.t
lib/Math/BigInt/t/upgrade.inc
lib/Math/BigInt/t/upgrade.t
lib/Math/BigInt/t/with_sub.t

index 42eb77c..7fceee8 100644 (file)
@@ -12,7 +12,7 @@ package Math::BigFloat;
 #   _a : accuracy
 #   _p : precision
 
-$VERSION = '1.46';
+$VERSION = '1.47';
 require 5.005;
 
 require Exporter;
@@ -132,7 +132,8 @@ sub new
     $self->{sign} = $wanted->sign();
     return $self->bnorm();
     }
-  # got string
+  # else: got a string
+
   # handle '+inf', '-inf' first
   if ($wanted =~ /^[+-]?inf$/)
     {
@@ -146,6 +147,17 @@ sub new
     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 @@ sub new
       ($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 @@ sub bpow
     ($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 @@ sub bpow
   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
 
index b84ad36..a6083e1 100644 (file)
@@ -18,7 +18,7 @@ package Math::BigInt;
 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 @@ use overload
 '|='   =>      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 @@ use overload
 '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 @@ sub bpow
 
   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
 
index a4a1002..3d53b0c 100644 (file)
@@ -6,7 +6,7 @@ use strict;
 
 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 @@ sub api_version () { 1; }
  
 # 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 @@ sub _base_len
     $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 @@ 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 $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 @@ sub _from_hex
     $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;
index cdf0f8f..a79dff1 100644 (file)
@@ -27,7 +27,7 @@ BEGIN
     }
   print "# INC = @INC\n";
 
-  plan tests => 1861;
+  plan tests => 1924;
   }
 
 use Math::BigFloat lib => 'BareCalc';
index 4f8b0ae..6695492 100644 (file)
@@ -26,7 +26,7 @@ BEGIN
     }
   print "# INC = @INC\n";
 
-  plan tests => 2848;
+  plan tests => 2952;
   }
 
 use Math::BigInt lib => 'BareCalc';
index c978644..131e453 100644 (file)
@@ -300,6 +300,29 @@ ok ($x ** $y, 0, 'no warnings and zero result');
 $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 @@ fnormNaN:NaN
 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
index 331621c..238a23f 100755 (executable)
@@ -26,7 +26,7 @@ BEGIN
     }
   print "# INC = @INC\n";
 
-  plan tests => 1861
+  plan tests => 1924
        + 2;            # own tests
   }
 
index 77b55b9..6453879 100644 (file)
@@ -476,6 +476,14 @@ $x = $class->new('1_000_000_000_000');
 ($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 @@ NaN:-inf:
 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 @@ E23:NaN
 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 @@ abc:12:NaN
 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
index ba0b314..6cd19f9 100755 (executable)
@@ -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 => 2848;
+  plan tests => 2952;
   }
 
 use Math::BigInt;
index 0dae63e..e9209b7 100755 (executable)
@@ -26,7 +26,7 @@ BEGIN
     }
   print "# INC = @INC\n"; 
   
-  plan tests => 1861
+  plan tests => 1924
     + 6;       # + our own tests
   }
 
index 69abaae..ee48b81 100755 (executable)
@@ -26,7 +26,7 @@ BEGIN
     }
   print "# INC = @INC\n";
 
-  plan tests => 2848
+  plan tests => 2952
     + 5;       # +5 own tests
   }
 
index 4799420..aac4a05 100644 (file)
@@ -1282,7 +1282,8 @@ abc:12:NaN
 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)
index a06aec3..ac137c1 100644 (file)
@@ -26,7 +26,7 @@ BEGIN
     }
   print "# INC = @INC\n";
 
-  plan tests => 2098
+  plan tests => 2100
    + 2;                        # our own tests
   }
 
index f34b887..8611e45 100644 (file)
@@ -28,7 +28,7 @@ BEGIN
     }
   print "# INC = @INC\n";
 
-  plan tests => 1861
+  plan tests => 1924
        + 1;
   }