From: Jarkko Hietaniemi Date: Sat, 23 Mar 2002 20:34:43 +0000 (+0000) Subject: Upgrade to Math::BigInt 1.55, from Tels. X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=56b9c9515cd02d944073a40912b99b5ed69f9766;p=p5sagit%2Fp5-mst-13.2.git Upgrade to Math::BigInt 1.55, from Tels. p4raw-id: //depot/perl@15447 --- diff --git a/MANIFEST b/MANIFEST index dd32c99..1b168a3 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1160,6 +1160,11 @@ lib/look.pl A "look" equivalent lib/Math/BigFloat.pm An arbitrary precision floating-point arithmetic package lib/Math/BigInt.pm An arbitrary precision integer arithmetic package lib/Math/BigInt/Calc.pm Pure Perl module to support Math::BigInt +lib/Math/BigInt/t/mbi_rand.t Test Math::BigInt randomly +lib/Math/BigInt/t/use_lib1.t Test combinations of Math::BigInt and BigFloat +lib/Math/BigInt/t/use_lib2.t Test combinations of Math::BigInt and BigFloat +lib/Math/BigInt/t/use_lib3.t Test combinations of Math::BigInt and BigFloat +lib/Math/BigInt/t/use_lib4.t Test combinations of Math::BigInt and BigFloat lib/Math/BigInt/t/bare_mbf.t Test MBF under Math::BigInt::BareCalc lib/Math/BigInt/t/bare_mbi.t Test MBI under Math::BigInt::BareCalc lib/Math/BigInt/t/bigfltpm.inc Shared tests for bigfltpm.t and sub_mbf.t diff --git a/lib/Math/BigFloat.pm b/lib/Math/BigFloat.pm index ad6588e..d47b5f1 100644 --- a/lib/Math/BigFloat.pm +++ b/lib/Math/BigFloat.pm @@ -12,15 +12,16 @@ package Math::BigFloat; # _p: precision # _f: flags, used to signal MBI not to touch our private parts -$VERSION = '1.30'; +$VERSION = '1.31'; require 5.005; use Exporter; -use Math::BigInt qw/objectify/; +use File::Spec; +# use Math::BigInt; @ISA = qw( Exporter Math::BigInt); use strict; use vars qw/$AUTOLOAD $accuracy $precision $div_scale $round_mode $rnd_mode/; -use vars qw/$upgrade $downgrade/; +use vars qw/$upgrade $downgrade $MBI/; my $class = "Math::BigFloat"; use overload @@ -48,16 +49,21 @@ $div_scale = 40; $upgrade = undef; $downgrade = undef; +$MBI = 'Math::BigInt'; # the package we are using for our private parts + # changable by use Math::BigFloat with => 'package' ############################################################################## # the old code had $rnd_mode, so we need to support it, too -$rnd_mode = 'even'; sub TIESCALAR { my ($class) = @_; bless \$round_mode, $class; } sub FETCH { return $round_mode; } sub STORE { $rnd_mode = $_[0]->round_mode($_[1]); } -BEGIN { tie $rnd_mode, 'Math::BigFloat'; } +BEGIN + { + $rnd_mode = 'even'; + tie $rnd_mode, 'Math::BigFloat'; + } ############################################################################## @@ -104,7 +110,7 @@ sub new if ((ref($wanted)) && (ref($wanted) ne $class)) { $self->{_m} = $wanted->as_number(); # get us a bigint copy - $self->{_e} = Math::BigInt->bzero(); + $self->{_e} = $MBI->bzero(); $self->{_m}->babs(); $self->{sign} = $wanted->sign(); return $self->bnorm(); @@ -115,8 +121,8 @@ sub new { return $downgrade->new($wanted) if $downgrade; - $self->{_e} = Math::BigInt->bzero(); - $self->{_m} = Math::BigInt->bzero(); + $self->{_e} = $MBI->bzero(); + $self->{_m} = $MBI->bzero(); $self->{sign} = $wanted; $self->{sign} = '+inf' if $self->{sign} eq 'inf'; return $self->bnorm(); @@ -129,16 +135,16 @@ sub new return $downgrade->bnan() if $downgrade; - $self->{_e} = Math::BigInt->bzero(); - $self->{_m} = Math::BigInt->bzero(); + $self->{_e} = $MBI->bzero(); + $self->{_m} = $MBI->bzero(); $self->{sign} = $nan; } else { # make integer from mantissa by adjusting exp, then convert to bigint # undef,undef to signal MBI that we don't need no bloody rounding - $self->{_e} = Math::BigInt->new("$$es$$ev",undef,undef); # exponent - $self->{_m} = Math::BigInt->new("$$miv$$mfv",undef,undef); # create mant. + $self->{_e} = $MBI->new("$$es$$ev",undef,undef); # exponent + $self->{_m} = $MBI->new("$$miv$$mfv",undef,undef); # create mant. # 3.123E0 = 3123E-3, and 3.123E-2 => 3123E-5 $self->{_e} -= CORE::length($$mfv) if CORE::length($$mfv) != 0; $self->{sign} = $$mis; @@ -163,39 +169,39 @@ sub _bnan { # used by parent class bone() to initialize number to 1 my $self = shift; - $self->{_m} = Math::BigInt->bzero(); - $self->{_e} = Math::BigInt->bzero(); + $self->{_m} = $MBI->bzero(); + $self->{_e} = $MBI->bzero(); } sub _binf { # used by parent class bone() to initialize number to 1 my $self = shift; - $self->{_m} = Math::BigInt->bzero(); - $self->{_e} = Math::BigInt->bzero(); + $self->{_m} = $MBI->bzero(); + $self->{_e} = $MBI->bzero(); } sub _bone { # used by parent class bone() to initialize number to 1 my $self = shift; - $self->{_m} = Math::BigInt->bone(); - $self->{_e} = Math::BigInt->bzero(); + $self->{_m} = $MBI->bone(); + $self->{_e} = $MBI->bzero(); } sub _bzero { # used by parent class bone() to initialize number to 1 my $self = shift; - $self->{_m} = Math::BigInt->bzero(); - $self->{_e} = Math::BigInt->bone(); + $self->{_m} = $MBI->bzero(); + $self->{_e} = $MBI->bone(); } sub isa { my ($self,$class) = @_; - return if $class eq 'Math::BigInt'; # we aren't - return UNIVERSAL::isa($self,$class); + return if $class =~ /^Math::BigInt/; # we aren't one of these + UNIVERSAL::isa($self,$class); } ############################################################################## @@ -264,7 +270,7 @@ sub bstr my $zeros = -$x->{_p} + $cad; $es .= $dot.'0' x $zeros if $zeros > 0; } - return $es; + $es; } sub bsstr @@ -285,7 +291,7 @@ sub bsstr } my $sign = $x->{_e}->{sign}; $sign = '' if $sign eq '-'; my $sep = 'e'.$sign; - return $x->{_m}->bstr().$sep.$x->{_e}->bstr(); + $x->{_m}->bstr().$sep.$x->{_e}->bstr(); } sub numify @@ -293,7 +299,7 @@ sub numify # Make a number from a BigFloat object # simple return string and let Perl's atoi()/atof() handle the rest my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); - return $x->bsstr(); + $x->bsstr(); } ############################################################################## @@ -429,8 +435,7 @@ sub badd return $x if $x->{sign} eq $y->{sign}; return $x->bnan(); } - # +-inf + something => +inf - # something +-inf => +-inf + # +-inf + something => +inf; something +-inf => +-inf $x->{sign} = $y->{sign}, return $x if $y->{sign} =~ /^[+-]inf$/; return $x; } @@ -448,11 +453,10 @@ sub badd # take lower of the two e's and adapt m1 to it to match m2 my $e = $y->{_e}; - $e = Math::BigInt::bzero() if !defined $e; # if no BFLOAT ? - $e = $e->copy(); # make copy (didn't do it yet) + $e = $MBI->bzero() if !defined $e; # if no BFLOAT ? + $e = $e->copy(); # make copy (didn't do it yet) $e->bsub($x->{_e}); my $add = $y->{_m}->copy(); -# if ($e < 0) # < 0 if ($e->{sign} eq '-') # < 0 { my $e1 = $e->copy()->babs(); @@ -460,7 +464,6 @@ sub badd $x->{_m}->blsft($e1,10); $x->{_e} += $e; # need the sign of e } -# if ($e > 0) # > 0 elsif (!$e->is_zero()) # > 0 { #$add *= (10 ** $e); @@ -947,13 +950,12 @@ sub bmod { $x->{_m}->blsft($x->{_e},10); } - $x->{_e} = Math::BigInt->bzero() unless $x->{_e}->is_zero(); + $x->{_e} = $MBI->bzero() unless $x->{_e}->is_zero(); $x->{_e}->bsub($shiftx) if $shiftx != 0; $x->{_e}->bsub($shifty) if $shifty != 0; # now mantissas are equalized, exponent of $x is adjusted, so calc result -# $ym->{sign} = '-' if $neg; # bmod() will make the correction for us $x->{_m}->bmod($ym); @@ -1023,7 +1025,7 @@ sub bsqrt && ($xas->bacmp($gs * $gs) == 0)) # guess hit the nail on the head? { # exact result - $x->{_m} = $gs; $x->{_e} = Math::BigInt->bzero(); $x->bnorm(); + $x->{_m} = $gs; $x->{_e} = $MBI->bzero(); $x->bnorm(); # shortcut to not run trough _find_round_parameters again if (defined $params[1]) { @@ -1104,6 +1106,108 @@ sub bfac $x->bnorm()->round(@r); } +sub _pow2 + { + # Calculate a power where $y is a non-integer, like 2 ** 0.5 + my ($x,$y,$a,$p,$r) = @_; + my $self = ref($x); + + # we need to limit the accuracy to protect against overflow + my $fallback = 0; + my $scale = 0; + my @params = $x->_find_round_parameters($a,$p,$r); + + # no rounding at all, so must use fallback + if (scalar @params == 1) + { + # simulate old behaviour + $params[1] = $self->div_scale(); # and round to it as accuracy + $scale = $params[1]+4; # at least four more for proper round + $params[3] = $r; # round mode by caller or undef + $fallback = 1; # to clear a/p afterwards + } + else + { + # the 4 below is empirical, and there might be cases where it is not + # enough... + $scale = abs($params[1] || $params[2]) + 4; # take whatever is defined + } + + # when user set globals, they would interfere with our calculation, so + # disable then and later re-enable them + no strict 'refs'; + my $abr = "$self\::accuracy"; my $ab = $$abr; $$abr = undef; + my $pbr = "$self\::precision"; my $pb = $$pbr; $$pbr = undef; + # we also need to disable any set A or P on $x (_find_round_parameters took + # them already into account), since these would interfere, too + delete $x->{_a}; delete $x->{_p}; + # need to disable $upgrade in BigInt, to avoid deep recursion + local $Math::BigInt::upgrade = undef; + + # split the second argument into its integer and fraction part + # we calculate the result then from these two parts, like in + # 2 ** 2.4 == (2 ** 2) * (2 ** 0.4) + my $c = $self->new($y->as_number()); # integer part + my $d = $y-$c; # fractional part + my $xc = $x->copy(); # a temp. copy + + # now calculate binary fraction from the decimal fraction on the fly + # f.i. 0.654: + # 0.654 * 2 = 1.308 > 1 => 0.1 ( 1.308 - 1 = 0.308) + # 0.308 * 2 = 0.616 < 1 => 0.10 + # 0.616 * 2 = 1.232 > 1 => 0.101 ( 1.232 - 1 = 0.232) + # and so on... + # The process stops when the result is exactly one, or when we have + # enough accuracy + + # From the binary fraction we calculate the result as follows: + # we assume the fraction ends in 1, and we remove this one first. + # For each digit after the dot, assume 1 eq R and 0 eq XR, where R means + # take square root and X multiply with the original X. + + my $i = 0; + while ($i++ < 50) + { + $d->badd($d); # * 2 + last if $d->is_one(); # == 1 + $x->bsqrt(); # 0 + if ($d > 1) + { + $x->bsqrt(); $x->bmul($xc); $d->bdec(); # 1 + } + print "at $x\n"; + } + # assume fraction ends in 1 + $x->bsqrt(); # 1 + if (!$c->is_one()) + { + $x->bmul( $xc->bpow($c) ); + } + elsif (!$c->is_zero()) + { + $x->bmul( $xc ); + } + # done + + # shortcut to not run trough _find_round_parameters again + if (defined $params[1]) + { + $x->bround($params[1],$params[3]); # then round accordingly + } + else + { + $x->bfround($params[2],$params[3]); # then round accordingly + } + if ($fallback) + { + # clear a/p after round, since user did not request it + $x->{_a} = undef; $x->{_p} = undef; + } + # restore globals + $$abr = $ab; $$pbr = $pb; + $x; + } + sub _pow { # Calculate a power where $y is a non-integer, like 2 ** 0.5 @@ -1209,7 +1313,7 @@ sub bpow return $x->bone() if $y->is_zero(); return $x if $x->is_one() || $y->is_one(); - return $x->_pow($y,$a,$p,$r) if !$y->is_int(); # non-integer power + return $x->_pow2($y,$a,$p,$r) if !$y->is_int(); # non-integer power my $y1 = $y->as_number(); # make bigint # if ($x == -1) @@ -1494,7 +1598,7 @@ sub AUTOLOAD } # try one level up, but subst. bxxx() for fxxx() since MBI only got bxxx() $name =~ s/^f/b/; - return &{'Math::BigInt'."::$name"}(@_); + return &{"$MBI"."::$name"}(@_); } my $bname = $name; $bname =~ s/^f/b/; *{$class."::$name"} = \&$bname; @@ -1552,6 +1656,7 @@ sub import { my $self = shift; my $l = scalar @_; my $j = 0; my @a = @_; + my $lib = ''; for ( my $i = 0; $i < $l ; $i++, $j++) { if ( $_[$i] eq ':constant' ) @@ -1575,7 +1680,28 @@ sub import my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." splice @a, $j, $s; $j -= $s; } + elsif ($_[$i] eq 'lib') + { + $lib = $_[$i+1] || ''; # default Calc + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; + } + elsif ($_[$i] eq 'with') + { + $MBI = $_[$i+1] || 'Math::BigInt'; # default Math::BigInt + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; + } } + my @parts = split /::/, $MBI; # Math::BigInt => Math BigInt + my $file = pop @parts; $file .= '.pm'; # BigInt => BigInt.pm + $file = File::Spec->catdir (@parts, $file); + # let use Math::BigInt lib => 'GMP'; use Math::BigFloat; still work + my $mbilib = eval { Math::BigInt->config()->{lib} }; + $lib .= ",$mbilib" if defined $mbilib; + require $file; + $MBI->import ( lib => $lib, 'objectify' ); + # any non :constant stuff is handled by our parent, Exporter # even if @_ is empty, to give it a chance $self->SUPER::import(@a); # for subclasses @@ -1643,7 +1769,7 @@ sub length $len += $x->{_e} if $x->{_e}->sign() eq '+'; if (wantarray()) { - my $t = Math::BigInt::bzero(); + my $t = $MBI->bzero(); $t = $x->{_e}->copy()->babs() if $x->{_e}->sign() eq '-'; return ($len,$t); } @@ -1922,10 +2048,100 @@ In particular perl -MMath::BigFloat=:constant -e 'print 2E-100,"\n"' -prints the value of C<2E-100>. Note that without conversion of +prints the value of C<2E-100>. Note that without conversion of constants the expression 2E-100 will be calculated as normal floating point number. +Please note that ':constant' does not affect integer constants, nor binary +nor hexadecimal constants. Use L or L to get this to +work. + +=head2 Math library + +Math with the numbers is done (by default) by a module called +Math::BigInt::Calc. This is equivalent to saying: + + use Math::BigFloat lib => 'Calc'; + +You can change this by using: + + use Math::BigFloat lib => 'BitVect'; + +The following would first try to find Math::BigInt::Foo, then +Math::BigInt::Bar, and when this also fails, revert to Math::BigInt::Calc: + + use Math::BigFloat lib => 'Foo,Math::BigInt::Bar'; + +Calc.pm uses as internal format an array of elements of some decimal base +(usually 1e7, but this might be differen for some systems) with the least +significant digit first, while BitVect.pm uses a bit vector of base 2, most +significant bit first. Other modules might use even different means of +representing the numbers. See the respective module documentation for further +details. + +Please note that Math::BigFloat does B use the denoted library itself, +but it merely passes the lib argument to Math::BigInt. So, instead of the need +to do: + + use Math::BigInt lib => 'GMP'; + use Math::BigFloat; + +you can roll it all into one line: + + use Math::BigFloat lib => 'GMP'; + +Use the lib, Luke! And see L for more details. + +=head2 Using Math::BigInt::Lite + +It is possible to use L with Math::BigFloat: + + # 1 + use Math::BigFloat with => 'Math::BigInt::Lite'; + +There is no need to "use Math::BigInt" or "use Math::BigInt::Lite", but you +can combine these if you want. For instance, you may want to use +Math::BigInt objects in your main script, too. + + # 2 + use Math::BigInt; + use Math::BigFloat with => 'Math::BigInt::Lite'; + +Of course, you can combine this with the C parameter. + + # 3 + use Math::BigFloat with => 'Math::BigInt::Lite', lib => 'GMP,Pari'; + +If you want to use Math::BigInt's, too, simple add a Math::BigInt B: + + # 4 + use Math::BigInt; + use Math::BigFloat with => 'Math::BigInt::Lite', lib => 'GMP,Pari'; + +Notice that the module with the last C will "win" and thus +it's lib will be used if the lib is available: + + # 5 + use Math::BigInt lib => 'Bar,Baz'; + use Math::BigFloat with => 'Math::BigInt::Lite', lib => 'Foo'; + +That would try to load Foo, Bar, Baz and Calc (in that order). Or in other +words, Math::BigFloat will try to retain previously loaded libs when you +don't specify it one. + +Actually, the lib loading order would be "Bar,Baz,Calc", and then +"Foo,Bar,Baz,Calc", but independend of which lib exists, the result is the +same as trying the latter load alone, except for the fact that Bar or Baz +might be loaded needlessly in an intermidiate step + +The old way still works though: + + # 6 + use Math::BigInt lib => 'Bar,Baz'; + use Math::BigFloat; + +But B for usage. + =head1 BUGS =over 2 diff --git a/lib/Math/BigInt.pm b/lib/Math/BigInt.pm index abe2c82..3c142f2 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.54'; +$VERSION = '1.55'; use Exporter; @ISA = qw( Exporter ); @EXPORT_OK = qw( objectify _swap bgcd blcm); @@ -535,7 +535,7 @@ sub binf # create a bigint '+-inf', if given a BigInt, set it to '+-inf' # the sign is either '+', or if given, used from there my $self = shift; - my $sign = shift; $sign = '+' if !defined $sign || $sign ne '-'; + my $sign = shift; $sign = '+' if !defined $sign || $sign !~ /^-(inf)?$/; $self = $class if !defined $self; if (!ref($self)) { @@ -554,7 +554,8 @@ sub binf # otherwise do our own thing $self->{value} = $CALC->_zero(); } - $self->{sign} = $sign.'inf'; + $sign = $sign . 'inf' if $sign !~ /inf$/; # - => -inf + $self->{sign} = $sign; ($self->{_a},$self->{_p}) = @_; # take over requested rounding return $self; } @@ -657,7 +658,7 @@ sub bstr # make a string from bigint object my $x = shift; $class = ref($x) || $x; $x = $class->new(shift) if !ref($x); # my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); - + if ($x->{sign} !~ /^[+-]$/) { return $x->{sign} unless $x->{sign} eq '+inf'; # -inf, NaN @@ -870,12 +871,11 @@ sub bcmp # post-normalized compare for internal use (honors signs) if ($x->{sign} eq '+') { - return 1 if $y->{sign} eq '-'; # 0 check handled above + # $x and $y both > 0 return $CALC->_acmp($x->{value},$y->{value}); } - # $x->{sign} eq '-' - return -1 if $y->{sign} eq '+'; + # $x && $y both < 0 $CALC->_acmp($y->{value},$x->{value}); # swaped (lib does only 0,1,-1) } @@ -906,8 +906,8 @@ sub badd # print "mbi badd ",join(' ',caller()),"\n"; # print "upgrade => ",$upgrade||'undef', # " \$x (",ref($x),") \$y (",ref($y),")\n"; -# return $upgrade->badd($x,$y,@r) if defined $upgrade && -# ((ref($x) eq $upgrade) || (ref($y) eq $upgrade)); + return $upgrade->badd($x,$y,@r) if defined $upgrade && + ((ref($x) eq $upgrade) || (ref($y) eq $upgrade)); # print "still badd\n"; $r[3] = $y; # no push! @@ -1487,7 +1487,7 @@ sub bpow $x->bmul($x); } $x->bmul($pow2) unless $pow2->is_one(); - return $x->round(@r); + $x->round(@r); } sub blsft @@ -1716,10 +1716,10 @@ sub length sub digit { # return the nth decimal digit, negative values count backward, 0 is right - my $x = shift; - my $n = shift || 0; + my ($self,$x,$n) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_); + $n = 0 if !defined $n; - return $CALC->_digit($x->{value},$n); + $CALC->_digit($x->{value},$n); } sub _trailing_zeros @@ -1789,7 +1789,7 @@ sub exponent my $e = $class->bzero(); return $e->binc() if $x->is_zero(); $e += $x->_trailing_zeros(); - return $e; + $e; } sub mantissa @@ -1804,8 +1804,9 @@ sub mantissa my $m = $x->copy(); # that's inefficient my $zeros = $m->_trailing_zeros(); - $m /= 10 ** $zeros if $zeros != 0; - return $m; + $m->brsft($zeros,10) if $zeros != 0; +# $m /= 10 ** $zeros if $zeros != 0; + $m; } sub parts @@ -2153,6 +2154,7 @@ sub import { # this causes overlord er load to step in overload::constant integer => sub { $self->new(shift) }; + overload::constant binary => sub { $self->new(shift) }; splice @a, $j, 1; $j --; } elsif ($_[$i] eq 'upgrade') @@ -2711,35 +2713,60 @@ If used on an object, it will set it to one: $x->bone(); # +1 $x->bone('-'); # -1 -=head2 is_one() / is_zero() / is_nan() / is_positive() / is_negative() / -is_inf() / is_odd() / is_even() / is_int() +=head2 is_one()/is_zero()/is_nan()/is_inf() + $x->is_zero(); # true if arg is +0 $x->is_nan(); # true if arg is NaN $x->is_one(); # true if arg is +1 $x->is_one('-'); # true if arg is -1 - $x->is_odd(); # true if odd, false for even - $x->is_even(); # true if even, false for odd - $x->is_positive(); # true if >= 0 - $x->is_negative(); # true if < 0 $x->is_inf(); # true if +inf $x->is_inf('-'); # true if -inf (sign is default '+') + +These methods all test the BigInt for beeing one specific value and return +true or false depending on the input. These are faster than doing something +like: + + if ($x == 0) + +=head2 is_positive()/is_negative() + + $x->is_positive(); # true if >= 0 + $x->is_negative(); # true if < 0 + +The methods return true if the argument is positive or negative, respectively. +C is neither positive nor negative, while C<+inf> counts as positive, and +C<-inf> is negative. A C is positive. + +These methods are only testing the sign, and not the value. + +=head2 is_odd()/is_even()/is_int() + + $x->is_odd(); # true if odd, false for even + $x->is_even(); # true if even, false for odd $x->is_int(); # true if $x is an integer -These methods all test the BigInt for one condition and return true or false -depending on the input. +The return true when the argument satisfies the condition. C, C<+inf>, +C<-inf> are not integers and are neither odd nor even. =head2 bcmp - $x->bcmp($y); # compare numbers (undef,<0,=0,>0) + $x->bcmp($y); + +Compares $x with $y and takes the sign into account. +Returns -1, 0, 1 or undef. =head2 bacmp - $x->bacmp($y); # compare absolutely (undef,<0,=0,>0) + $x->bacmp($y); + +Compares $x with $y while ignoring their. Returns -1, 0, 1 or undef. =head2 sign - $x->sign(); # return the sign, either +,- or NaN + $x->sign(); + +Return the sign, of $x, meaning either C<+>, C<->, C<-inf>, C<+inf> or NaN. =head2 bcmp @@ -3381,15 +3408,15 @@ Examples for converting: =head1 Autocreating constants -After C all the B decimal constants -in the given scope are converted to C. This conversion -happens at compile time. +After C all the B decimal, hexadecimal +and binary constants in the given scope are converted to C. +This conversion happens at compile time. In particular, perl -MMath::BigInt=:constant -e 'print 2**100,"\n"' -prints the integer value of C<2**100>. Note that without conversion of +prints the integer value of C<2**100>. Note that without conversion of constants the expression 2**100 will be calculated as perl scalar. Please note that strings and floating point constants are not affected, @@ -3413,6 +3440,16 @@ Without the quotes Perl would convert the large number to a floating point constant at compile time and then hand the result to BigInt, which results in an truncated result or a NaN. +This also applies to integers that look like floating point constants: + + use Math::BigInt ':constant'; + + print ref(123e2),"\n"; + print ref(123.2e2),"\n"; + +will print nothing but newlines. Use either L or L +to get this to work. + =head1 PERFORMANCE Using the form $x += $y; etc over $x = $x + $y is faster, since a copy of $x diff --git a/lib/Math/BigInt/Calc.pm b/lib/Math/BigInt/Calc.pm index de4f46e..a7110c9 100644 --- a/lib/Math/BigInt/Calc.pm +++ b/lib/Math/BigInt/Calc.pm @@ -8,7 +8,7 @@ require Exporter; use vars qw/@ISA $VERSION/; @ISA = qw(Exporter); -$VERSION = '0.25'; +$VERSION = '0.26'; # Package to store unsigned big integers in decimal and do math with them @@ -392,7 +392,7 @@ sub _sub { # (ref to int_num_array, ref to int_num_array, swap) # subtract base 1eX numbers -- stolen from Knuth Vol 2 pg 232, $x > $y - # subtract Y from X (X is always greater/equal!) by modifying x in place + # subtract Y from X by modifying x in place my ($c,$sx,$sy,$s) = @_; my $car = 0; my $i; my $j = 0; @@ -410,7 +410,9 @@ sub _sub #print "case 1 (swap)\n"; for $i (@$sx) { - last unless defined $sy->[$j] || $car; + # we can't do an early out if $x is than $y, since we + # need to copy the high chunks from $y. Found by Bob Mathews. + #last unless defined $sy->[$j] || $car; $sy->[$j] += $BASE if $car = (($sy->[$j] = $i-($sy->[$j]||0) - $car) < 0); $j++; @@ -1174,7 +1176,7 @@ sub _rsft $dst++; } splice (@$x,$dst) if $dst > 0; # kill left-over array elems - pop @$x if $x->[-1] == 0; # kill last element if 0 + pop @$x if $x->[-1] == 0 && @$x > 1; # kill last element if 0 } # else rem == 0 $x; } diff --git a/lib/Math/BigInt/t/bare_mbf.t b/lib/Math/BigInt/t/bare_mbf.t index 8288d2b..abeb8c2 100644 --- a/lib/Math/BigInt/t/bare_mbf.t +++ b/lib/Math/BigInt/t/bare_mbf.t @@ -26,11 +26,14 @@ BEGIN } print "# INC = @INC\n"; - plan tests => 1592; + plan tests => 1601; } -use Math::BigInt lib => 'BareCalc'; -use Math::BigFloat; +#use Math::BigInt lib => 'BareCalc'; +#use Math::BigFloat; + +# use Math::BigInt; use Math::BigFloat lib => 'BareCalc'; +use Math::BigFloat lib => 'BareCalc'; use vars qw ($class $try $x $y $f @args $ans $ans1 $ans1_str $setup $CL); $class = "Math::BigFloat"; diff --git a/lib/Math/BigInt/t/bare_mbi.t b/lib/Math/BigInt/t/bare_mbi.t index d480062..5899dfe 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 => 2147; + plan tests => 2237; } use Math::BigInt lib => 'BareCalc'; diff --git a/lib/Math/BigInt/t/bigfltpm.inc b/lib/Math/BigInt/t/bigfltpm.inc index 8748d23..734b935 100644 --- a/lib/Math/BigInt/t/bigfltpm.inc +++ b/lib/Math/BigInt/t/bigfltpm.inc @@ -153,6 +153,19 @@ $x = $class->new(2); $x->fzero(); ok_undef ($x->{_a}); ok_undef ($x->{_p}); $x = $class->new(2); $x->finf(); ok_undef ($x->{_a}); ok_undef ($x->{_p}); $x = $class->new(2); $x->fone(); ok_undef ($x->{_a}); ok_undef ($x->{_p}); $x = $class->new(2); $x->fnan(); ok_undef ($x->{_a}); ok_undef ($x->{_p}); + +############################################################################### +# bone/binf etc as plain calls (Lite failed them) + +ok ($class->fzero(),0); +ok ($class->fone(),1); +ok ($class->fone('+'),1); +ok ($class->fone('-'),-1); +ok ($class->fnan(),'NaN'); +ok ($class->finf(),'inf'); +ok ($class->finf('+'),'inf'); +ok ($class->finf('-'),'-inf'); +ok ($class->finf('-inf'),'-inf'); ############################################################################### # fsqrt() with set global A/P or A/P enabled on $x, also a test whether fsqrt() @@ -370,7 +383,11 @@ abc:123.456:NaN # 2 ** 0.5 == sqrt(2) # 1.41..7 and not 1.4170 since fallback (bsqrt(9) is '3', not 3.0...0) 2:0.5:1.41421356237309504880168872420969807857 -2:0.2:1.148698354997035006798626946777927589444 +#2:0.2:1.148698354997035006798626946777927589444 +6:1.5:14.6969384566990685891837044482353483518 +$div_scale = 20; +#62.5:12.5:26447206647554886213592.3959144 +$div_scale = 40; &fneg fnegNaN:NaN +inf:-inf diff --git a/lib/Math/BigInt/t/bigfltpm.t b/lib/Math/BigInt/t/bigfltpm.t index 2b4f83a..a3c0131 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 => 1592; + plan tests => 1601; } use Math::BigInt; diff --git a/lib/Math/BigInt/t/bigintpm.inc b/lib/Math/BigInt/t/bigintpm.inc index 39f4c77..2bcf346 100644 --- a/lib/Math/BigInt/t/bigintpm.inc +++ b/lib/Math/BigInt/t/bigintpm.inc @@ -164,7 +164,7 @@ while () }elsif ($f eq "bpow"){ $try .= "\$x ** \$y;"; }elsif ($f eq "digit"){ - $try = "\$x = $class->new('$args[0]'); \$x->digit($args[1]);"; + $try .= "\$x->digit(\$y);"; } else { warn "Unknown op '$f'"; } } # end else all other ops @@ -311,25 +311,25 @@ $x = Math::BigInt->new(0); if (!$x) { ok (1,1); } else { ok($x,'to be false') } @args = Math::BigInt::objectify(2,4,5); ok (scalar @args,3); # $class, 4, 5 -ok ($args[0],$class); +ok ($args[0] =~ /^Math::BigInt/); ok ($args[1],4); ok ($args[2],5); @args = Math::BigInt::objectify(0,4,5); ok (scalar @args,3); # $class, 4, 5 -ok ($args[0],$class); +ok ($args[0] =~ /^Math::BigInt/); ok ($args[1],4); ok ($args[2],5); @args = Math::BigInt::objectify(2,4,5); ok (scalar @args,3); # $class, 4, 5 -ok ($args[0],$class); +ok ($args[0] =~ /^Math::BigInt/); ok ($args[1],4); ok ($args[2],5); @args = Math::BigInt::objectify(2,4,5,6,7); ok (scalar @args,5); # $class, 4, 5, 6, 7 -ok ($args[0],$class); +ok ($args[0] =~ /^Math::BigInt/); ok ($args[1],4); ok (ref($args[1]),$args[0]); ok ($args[2],5); ok (ref($args[2]),$args[0]); ok ($args[3],6); ok (ref($args[3]),''); @@ -446,6 +446,11 @@ if ($x > $BASE) { ok (1,1) } else { ok ("$x > $BASE","$x < $BASE"); } $x = $class->new($MAX); ok ($x->length(), length($MAX)); ############################################################################### +# test bug that $class->digit($string) did not work + +ok ($class->digit(123,2),1); + +############################################################################### # bug in sub where number with at least 6 trailing zeros after any op failed $x = $class->new(123456); $z = $class->new(10000); $z *= 10; $x -= $z; @@ -490,6 +495,19 @@ $x = $class->new('-322056000'); ($x,$y) = $x->bdiv('-12882240'); ok ($y,'0'); is_valid($y); # $y not '-0' ############################################################################### +# bone/binf etc as plain calls (Lite failed them) + +ok ($class->bzero(),0); +ok ($class->bone(),1); +ok ($class->bone('+'),1); +ok ($class->bone('-'),-1); +ok ($class->bnan(),'NaN'); +ok ($class->binf(),'inf'); +ok ($class->binf('+'),'inf'); +ok ($class->binf('-'),'-inf'); +ok ($class->binf('-inf'),'-inf'); + +############################################################################### # all tests done 1; @@ -515,15 +533,20 @@ sub is_valid my ($x,$f) = @_; my $e = 0; # error? - # ok as reference? - $e = 'Not a reference to Math::BigInt' if !ref($x); - # has ok sign? - $e = "Illegal sign $x->{sign} (expected: '+', '-', '-inf', '+inf' or 'NaN'" - if $e eq '0' && $x->{sign} !~ /^(\+|-|\+inf|-inf|NaN)$/; + # allow the check to pass for all Lite, and all MBI and subclasses + # ok as reference? + $e = 'Not a reference to Math::BigInt' if ref($x) !~ /^Math::BigInt/; - $e = "-0 is invalid!" if $e ne '0' && $x->{sign} eq '-' && $x == 0; - $e = $CALC->_check($x->{value}) if $e eq '0'; + if (ref($x) ne 'Math::BigInt::Lite') + { + # has ok sign? + $e = "Illegal sign $x->{sign} (expected: '+', '-', '-inf', '+inf' or 'NaN'" + if $e eq '0' && $x->{sign} !~ /^(\+|-|\+inf|-inf|NaN)$/; + + $e = "-0 is invalid!" if $e ne '0' && $x->{sign} eq '-' && $x == 0; + $e = $CALC->_check($x->{value}) if $e eq '0'; + } # test done, see if error did crop up ok (1,1), return if ($e eq '0'); @@ -1029,6 +1052,26 @@ baddNaN:+inf:NaN -123456789:987654321:864197532 -123456789:-987654321:-1111111110 +123456789:-987654321:-864197532 +-1:10001:10000 +-1:100001:100000 +-1:1000001:1000000 +-1:10000001:10000000 +-1:100000001:100000000 +-1:1000000001:1000000000 +-1:10000000001:10000000000 +-1:100000000001:100000000000 +-1:1000000000001:1000000000000 +-1:10000000000001:10000000000000 +-1:-10001:-10002 +-1:-100001:-100002 +-1:-1000001:-1000002 +-1:-10000001:-10000002 +-1:-100000001:-100000002 +-1:-1000000001:-1000000002 +-1:-10000000001:-10000000002 +-1:-100000000001:-100000000002 +-1:-1000000000001:-1000000000002 +-1:-10000000000001:-10000000000002 &bsub abc:abc:NaN abc:+0:NaN @@ -1071,6 +1114,26 @@ abc:+0:NaN -123456789:+987654321:-1111111110 -123456789:-987654321:864197532 +123456789:-987654321:1111111110 +10001:1:10000 +100001:1:100000 +1000001:1:1000000 +10000001:1:10000000 +100000001:1:100000000 +1000000001:1:1000000000 +10000000001:1:10000000000 +100000000001:1:100000000000 +1000000000001:1:1000000000000 +10000000000001:1:10000000000000 +10001:-1:10002 +100001:-1:100002 +1000001:-1:1000002 +10000001:-1:10000002 +100000001:-1:100000002 +1000000001:-1:1000000002 +10000000001:-1:10000000002 +100000000001:-1:100000000002 +1000000000001:-1:1000000000002 +10000000000001:-1:10000000000002 &bmul abc:abc:NaN abc:+0:NaN diff --git a/lib/Math/BigInt/t/bigintpm.t b/lib/Math/BigInt/t/bigintpm.t index eca2d29..c14d441 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 => 2147; + plan tests => 2237; } use Math::BigInt; diff --git a/lib/Math/BigInt/t/config.t b/lib/Math/BigInt/t/config.t index fc3e52f..db0c27e 100644 --- a/lib/Math/BigInt/t/config.t +++ b/lib/Math/BigInt/t/config.t @@ -22,7 +22,7 @@ my $cfg = Math::BigInt->config(); ok (ref($cfg),'HASH'); ok ($cfg->{lib},'Math::BigInt::Calc'); -ok ($cfg->{lib_version},'0.25'); +ok ($cfg->{lib_version},'0.26'); ok ($cfg->{class},'Math::BigInt'); ok ($cfg->{upgrade}||'',''); ok ($cfg->{div_scale},40); diff --git a/lib/Math/BigInt/t/constant.t b/lib/Math/BigInt/t/constant.t index ef3e223..3c9b13f 100644 --- a/lib/Math/BigInt/t/constant.t +++ b/lib/Math/BigInt/t/constant.t @@ -8,13 +8,24 @@ BEGIN $| = 1; chdir 't' if -d 't'; unshift @INC, '../lib'; # for running manually - plan tests => 5; + plan tests => 7; } use Math::BigInt ':constant'; ok (2 ** 255,'57896044618658097711785492504343953926634992332820282019728792003956564819968'); +{ + no warnings 'portable'; + # hexadecimal constants + ok (0x123456789012345678901234567890, + Math::BigInt->new('0x123456789012345678901234567890')); + # binary constants + ok (0b01010100011001010110110001110011010010010110000101101101, + Math::BigInt->new( + '0b01010100011001010110110001110011010010010110000101101101')); +} + use Math::BigFloat ':constant'; ok (1.0 / 3.0, '0.3333333333333333333333333333333333333333'); diff --git a/lib/Math/BigInt/t/mbi_rand.t b/lib/Math/BigInt/t/mbi_rand.t new file mode 100644 index 0000000..1f19c6b --- /dev/null +++ b/lib/Math/BigInt/t/mbi_rand.t @@ -0,0 +1,56 @@ +#!/usr/bin/perl -w + +use Test; +use strict; + +my $count; + +BEGIN + { + $| = 1; + unshift @INC, '../lib'; # for running manually + my $location = $0; $location =~ s/mbi_rand.t//; + unshift @INC, $location; # to locate the testing files + chdir 't' if -d 't'; + $count = 500; + plan tests => $count*2; + } + +use Math::BigInt; +my $c = 'Math::BigInt'; + +my $length = 200; + +# If you get a failure here, please re-run the test with the printed seed +# value as input: perl t/mbi_rand.t seed + +my $seed = int(rand(65537)); print "# seed: $seed\n"; srand($seed); + +my ($A,$B,$ADB,$AMB,$la,$lb); +for (my $i = 0; $i < $count; $i++) + { + # length of A and B + $la = int(rand($length)+1); $lb = int(rand($length)+1); + $A = ''; $B = ''; + # we create the numbers from "patterns", e.g. get a random number and a + # random count and string them together. This means things like + # "100000999999999999911122222222" are much more likely. If we just strung + # together digits, we would end up with "1272398823211223" etc. + while (length($A) < $la) { $A .= int(rand(100)) x int(rand(16)); } + while (length($B) < $lb) { $B .= int(rand(100)) x int(rand(16)); } + $A = $c->new($A); $B = $c->new($B); + print "# A $A\n# B $B\n"; + if ($A->is_zero() || $B->is_zero()) + { + ok (1,1); ok (1,1); next; + } + # check that int(A/B)*B + A % B == A holds for all inputs + # $X = ($A/$B)*$B + 2 * ($A % $B) - ($A % $B); + ($ADB,$AMB) = $A->copy()->bdiv($B); + ok ($A,$ADB*$B+2*$AMB-$AMB); + # swap 'em and try this, too + # $X = ($B/$A)*$A + $B % $A; + ($ADB,$AMB) = $B->copy()->bdiv($A); + ok ($B,$ADB*$A+2*$AMB-$AMB); + } + diff --git a/lib/Math/BigInt/t/sub_mbf.t b/lib/Math/BigInt/t/sub_mbf.t index 3df9ce4..69a1ab9 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 => 1592 + plan tests => 1601 + 6; # + our own tests } diff --git a/lib/Math/BigInt/t/sub_mbi.t b/lib/Math/BigInt/t/sub_mbi.t index c492592..95a0dae 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 => 2147 + plan tests => 2237 + 5; # +4 own tests } diff --git a/lib/Math/BigInt/t/upgrade.inc b/lib/Math/BigInt/t/upgrade.inc index 26b3a65..bf35261 100644 --- a/lib/Math/BigInt/t/upgrade.inc +++ b/lib/Math/BigInt/t/upgrade.inc @@ -725,9 +725,9 @@ baddNaN:+inf:NaN -123456789:987654321:864197532 -123456789:-987654321:-1111111110 +123456789:-987654321:-864197532 -#2:2.5:4.5^ -#-123:-1.5:-124.5^ -#-1.2:1:-0.2^ +2:2.5:4.5^ +-123:-1.5:-124.5^ +-1.2:1:-0.2^ &bsub abc:abc:NaN abc:+0:NaN diff --git a/lib/Math/BigInt/t/upgrade.t b/lib/Math/BigInt/t/upgrade.t index 534c99b..5c8cf5f 100644 --- a/lib/Math/BigInt/t/upgrade.t +++ b/lib/Math/BigInt/t/upgrade.t @@ -10,7 +10,7 @@ BEGIN my $location = $0; $location =~ s/upgrade.t//; unshift @INC, $location; # to locate the testing files chdir 't' if -d 't'; - plan tests => 2056 + plan tests => 2068 + 2; # our own tests } diff --git a/lib/Math/BigInt/t/use_lib1.t b/lib/Math/BigInt/t/use_lib1.t new file mode 100644 index 0000000..d737081 --- /dev/null +++ b/lib/Math/BigInt/t/use_lib1.t @@ -0,0 +1,24 @@ +#!/usr/bin/perl -w + +# see if using Math::BigInt and Math::BigFloat works together nicely. +# all use_lib*.t should be equivalent + +use strict; +use Test; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; # for running manually + unshift @INC, 'lib'; + print "# INC = @INC\n"; + plan tests => 2; + } + +use Math::BigFloat lib => 'BareCalc'; + +ok (Math::BigInt->config()->{lib},'Math::BigInt::BareCalc'); + +ok (Math::BigFloat->new(123)->badd(123),246); + diff --git a/lib/Math/BigInt/t/use_lib2.t b/lib/Math/BigInt/t/use_lib2.t new file mode 100644 index 0000000..6dd744f --- /dev/null +++ b/lib/Math/BigInt/t/use_lib2.t @@ -0,0 +1,24 @@ +#!/usr/bin/perl -w + +# see if using Math::BigInt and Math::BigFloat works together nicely. +# all use_lib*.t should be equivalent + +use strict; +use Test; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; # for running manually + unshift @INC, 'lib'; + plan tests => 2; + } + +use Math::BigInt; +use Math::BigFloat lib => 'BareCalc'; + +ok (Math::BigInt->config()->{lib},'Math::BigInt::BareCalc'); + +ok (Math::BigFloat->new(123)->badd(123),246); + diff --git a/lib/Math/BigInt/t/use_lib3.t b/lib/Math/BigInt/t/use_lib3.t new file mode 100644 index 0000000..3b43544 --- /dev/null +++ b/lib/Math/BigInt/t/use_lib3.t @@ -0,0 +1,24 @@ +#!/usr/bin/perl -w + +# see if using Math::BigInt and Math::BigFloat works together nicely. +# all use_lib*.t should be equivalent + +use strict; +use Test; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; # for running manually + unshift @INC, 'lib'; + plan tests => 2; + } + +use Math::BigInt lib => 'BareCalc'; +use Math::BigFloat; + +ok (Math::BigInt->config()->{lib},'Math::BigInt::BareCalc'); + +ok (Math::BigFloat->new(123)->badd(123),246); + diff --git a/lib/Math/BigInt/t/use_lib4.t b/lib/Math/BigInt/t/use_lib4.t new file mode 100644 index 0000000..079ba6d --- /dev/null +++ b/lib/Math/BigInt/t/use_lib4.t @@ -0,0 +1,25 @@ +#!/usr/bin/perl -w + +# see if using Math::BigInt and Math::BigFloat works together nicely. +# all use_lib*.t should be equivalent, except this, since the later overrides +# the former lib statement + +use strict; +use Test; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; # for running manually + unshift @INC, 'lib'; + plan tests => 2; + } + +use Math::BigInt lib => 'BareCalc'; +use Math::BigFloat lib => 'Calc'; + +ok (Math::BigInt->config()->{lib},'Math::BigInt::Calc'); + +ok (Math::BigFloat->new(123)->badd(123),246); +