# underlying lib might change the reference!
my $class = "Math::BigInt";
-require 5.005;
+use 5.006002;
+
+$VERSION = '1.87';
+
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(objectify bgcd blcm);
-$VERSION = '1.68';
-use Exporter;
-@ISA = qw( Exporter );
-@EXPORT_OK = qw( objectify bgcd blcm);
# _trap_inf and _trap_nan are internal and should never be accessed from the
# outside
use vars qw/$round_mode $accuracy $precision $div_scale $rnd_mode
'^=' => sub { $_[0]->bxor($_[1]); },
'&=' => sub { $_[0]->band($_[1]); },
'|=' => sub { $_[0]->bior($_[1]); },
+
'**=' => sub { $_[0]->bpow($_[1]); },
+'<<=' => sub { $_[0]->blsft($_[1]); },
+'>>=' => sub { $_[0]->brsft($_[1]); },
# not supported by Perl yet
'..' => \&_pointpoint,
-'<=>' => sub { $_[2] ?
+'<=>' => sub { my $rc = $_[2] ?
ref($_[0])->bcmp($_[1],$_[0]) :
- $_[0]->bcmp($_[1])},
+ $_[0]->bcmp($_[1]);
+ $rc = 1 unless defined $rc;
+ $rc <=> 0;
+ },
+# we need '>=' to get things like "1 >= NaN" right:
+'>=' => sub { my $rc = $_[2] ?
+ ref($_[0])->bcmp($_[1],$_[0]) :
+ $_[0]->bcmp($_[1]);
+ # if there was a NaN involved, return false
+ return '' unless defined $rc;
+ $rc >= 0;
+ },
'cmp' => sub {
$_[2] ?
"$_[1]" cmp $_[0]->bstr() :
$_[0]->bstr() cmp "$_[1]" },
-# make cos()/sin()/exp() "work" with BigInt's or subclasses
-'cos' => sub { cos($_[0]->numify()) },
-'sin' => sub { sin($_[0]->numify()) },
-'exp' => sub { exp($_[0]->numify()) },
-'atan2' => sub { atan2($_[0]->numify(),$_[1]) },
+'cos' => sub { $_[0]->copy->bcos(); },
+'sin' => sub { $_[0]->copy->bsin(); },
+'atan2' => sub { $_[2] ?
+ atan2($_[1],$_[0]->numify()) :
+ atan2($_[0]->numify(),$_[1]) },
+
+# are not yet overloadable
+#'hex' => sub { print "hex"; $_[0]; },
+#'oct' => sub { print "oct"; $_[0]; },
-'log' => sub { $_[0]->copy()->blog($_[1]); },
+# log(N) is log(N, e), where e is Euler's number
+'log' => sub { $_[0]->copy()->blog($_[1], undef); },
+'exp' => sub { $_[0]->copy()->bexp($_[1]); },
'int' => sub { $_[0]->copy(); },
'neg' => sub { $_[0]->copy()->bneg(); },
'abs' => sub { $_[0]->copy()->babs(); },
'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's a bit tricky to not modify b: b-a => -a+b
'-' => sub { my $c = $_[0]->copy; $_[2] ?
- $c->bneg()->badd($_[1]) :
- $c->bsub( $_[1]) },
+ $c->bneg()->badd( $_[1]) :
+ $c->bsub( $_[1]) },
'+' => sub { $_[0]->copy()->badd($_[1]); },
'*' => sub { $_[0]->copy()->bmul($_[1]); },
'bool' => sub {
# this kludge is needed for perl prior 5.6.0 since returning 0 here fails :-/
# v5.6.1 dumps on this: return !$_[0]->is_zero() || undef; :-(
- my $t = !$_[0]->is_zero();
- undef $t if $t == 0;
+ my $t = undef;
+ $t = 1 if !$_[0]->is_zero();
$t;
},
##############################################################################
# global constants, flags and accessory
-# these are public, but their usage is not recommended, use the accessor
-# methods instead
+# These vars are public, but their direct usage is not recommended, use the
+# accessor methods instead
-$round_mode = 'even'; # one of 'even', 'odd', '+inf', '-inf', 'zero' or 'trunc'
+$round_mode = 'even'; # one of 'even', 'odd', '+inf', '-inf', 'zero', 'trunc' or 'common'
$accuracy = undef;
$precision = undef;
$div_scale = 40;
$upgrade = undef; # default is no upgrade
$downgrade = undef; # default is no downgrade
-# these are internally, and not to be used from the outside
-
-sub MB_NEVER_ROUND () { 0x0001; }
+# These are internally, and not to be used from the outside at all
$_trap_nan = 0; # are NaNs ok? set w/ config()
$_trap_inf = 0; # are infs ok? set w/ config()
my $nan = 'NaN'; # constants for easier life
-my $CALC = 'Math::BigInt::Calc'; # module to do the low level math
- # default is Calc.pm
-my %CAN; # cache for $CALC->can(...)
+my $CALC = 'Math::BigInt::FastCalc'; # module to do the low level math
+ # default is FastCalc.pm
my $IMPORT = 0; # was import() called yet?
# used to make require work
-
+my %WARN; # warn only once for low-level libs
+my %CAN; # cache for $CALC->can(...)
+my %CALLBACKS; # callbacks to notify on lib loads
my $EMU_LIB = 'Math/BigInt/CalcEmu.pm'; # emulate low-level math
-my $EMU = 'Math::BigInt::CalcEmu'; # emulate low-level math
##############################################################################
# the old code had $rnd_mode, so we need to support it, too
if (defined $_[0])
{
my $m = shift;
- if ($m !~ /^(even|odd|\+inf|\-inf|zero|trunc)$/)
+ if ($m !~ /^(even|odd|\+inf|\-inf|zero|trunc|common)$/)
{
require Carp; Carp::croak ("Unknown round mode '$m'");
}
# need to set new value?
if (@_ > 0)
{
- my $u = shift;
- return ${"${class}::upgrade"} = $u;
+ return ${"${class}::upgrade"} = $_[0];
}
${"${class}::upgrade"};
}
# need to set new value?
if (@_ > 0)
{
- my $u = shift;
- return ${"${class}::downgrade"} = $u;
+ return ${"${class}::downgrade"} = $_[0];
}
${"${class}::downgrade"};
}
{
require Carp; Carp::croak ('div_scale must be greater than zero');
}
- ${"${class}::div_scale"} = shift;
+ ${"${class}::div_scale"} = $_[0];
}
${"${class}::div_scale"};
}
if (ref($x))
{
# $object->accuracy() or fallback to global
- $x->bround($a) if $a; # not for undef, 0
- $x->{_a} = $a; # set/overwrite, even if not rounded
- $x->{_p} = undef; # clear P
+ $x->bround($a) if $a; # not for undef, 0
+ $x->{_a} = $a; # set/overwrite, even if not rounded
+ delete $x->{_p}; # clear P
$a = ${"${class}::accuracy"} unless defined $a; # proper return value
}
else
{
- # set global
- ${"${class}::accuracy"} = $a;
- ${"${class}::precision"} = undef; # clear P
+ ${"${class}::accuracy"} = $a; # set global A
+ ${"${class}::precision"} = undef; # clear global P
}
- return $a; # shortcut
+ return $a; # shortcut
}
- my $r;
+ my $a;
# $object->accuracy() or fallback to global
- $r = $x->{_a} if ref($x);
+ $a = $x->{_a} if ref($x);
# but don't return global undef, when $x's accuracy is 0!
- $r = ${"${class}::accuracy"} if !defined $r;
- $r;
+ $a = ${"${class}::accuracy"} if !defined $a;
+ $a;
}
sub precision
if (ref($x))
{
# $object->precision() or fallback to global
- $x->bfround($p) if $p; # not for undef, 0
- $x->{_p} = $p; # set/overwrite, even if not rounded
- $x->{_a} = undef; # clear A
+ $x->bfround($p) if $p; # not for undef, 0
+ $x->{_p} = $p; # set/overwrite, even if not rounded
+ delete $x->{_a}; # clear A
$p = ${"${class}::precision"} unless defined $p; # proper return value
}
else
{
- # set global
- ${"${class}::precision"} = $p;
- ${"${class}::accuracy"} = undef; # clear A
+ ${"${class}::precision"} = $p; # set global P
+ ${"${class}::accuracy"} = undef; # clear global A
}
- return $p; # shortcut
+ return $p; # shortcut
}
- my $r;
+ my $p;
# $object->precision() or fallback to global
- $r = $x->{_p} if ref($x);
+ $p = $x->{_p} if ref($x);
# but don't return global undef, when $x's precision is 0!
- $r = ${"${class}::precision"} if !defined $r;
- $r;
+ $p = ${"${class}::precision"} if !defined $p;
+ $p;
}
sub config
my $class = shift || 'Math::BigInt';
no strict 'refs';
- if (@_ > 0)
+ if (@_ > 1 || (@_ == 1 && (ref($_[0]) eq 'HASH')))
{
# try to set given options as arguments from hash
{
$cfg->{$key} = ${"${class}::$key"};
};
+ if (@_ == 1 && (ref($_[0]) ne 'HASH'))
+ {
+ # calls of the style config('lib') return just this value
+ return $cfg->{$_[0]};
+ }
$cfg;
}
{
# select accuracy parameter based on precedence,
# used by bround() and bfround(), may return undef for scale (means no op)
- my ($x,$s,$m,$scale,$mode) = @_;
- $scale = $x->{_a} if !defined $scale;
- $scale = $s if (!defined $scale);
- $mode = $m if !defined $mode;
- return ($scale,$mode);
+ my ($x,$scale,$mode) = @_;
+
+ $scale = $x->{_a} unless defined $scale;
+
+ no strict 'refs';
+ my $class = ref($x);
+
+ $scale = ${ $class . '::accuracy' } unless defined $scale;
+ $mode = ${ $class . '::round_mode' } unless defined $mode;
+
+ ($scale,$mode);
}
sub _scale_p
{
# select precision parameter based on precedence,
# used by bround() and bfround(), may return undef for scale (means no op)
- my ($x,$s,$m,$scale,$mode) = @_;
- $scale = $x->{_p} if !defined $scale;
- $scale = $s if (!defined $scale);
- $mode = $m if !defined $mode;
- return ($scale,$mode);
+ my ($x,$scale,$mode) = @_;
+
+ $scale = $x->{_p} unless defined $scale;
+
+ no strict 'refs';
+ my $class = ref($x);
+
+ $scale = ${ $class . '::precision' } unless defined $scale;
+ $mode = ${ $class . '::round_mode' } unless defined $mode;
+
+ ($scale,$mode);
}
##############################################################################
sub copy
{
- my ($c,$x);
+ # if two arguments, the first one is the class to "swallow" subclasses
if (@_ > 1)
{
- # if two arguments, the first one is the class to "swallow" subclasses
- ($c,$x) = @_;
- }
- else
- {
- $x = shift;
- $c = ref($x);
- }
- return unless ref($x); # only for objects
+ my $self = bless {
+ sign => $_[1]->{sign},
+ value => $CALC->_copy($_[1]->{value}),
+ }, $_[0] if @_ > 1;
- my $self = {}; bless $self,$c;
- my $r;
- foreach my $k (keys %$x)
- {
- if ($k eq 'value')
- {
- $self->{value} = $CALC->_copy($x->{value}); next;
- }
- if (!($r = ref($x->{$k})))
- {
- $self->{$k} = $x->{$k}; next;
- }
- if ($r eq 'SCALAR')
- {
- $self->{$k} = \${$x->{$k}};
- }
- elsif ($r eq 'ARRAY')
- {
- $self->{$k} = [ @{$x->{$k}} ];
- }
- elsif ($r eq 'HASH')
- {
- # only one level deep!
- foreach my $h (keys %{$x->{$k}})
- {
- $self->{$k}->{$h} = $x->{$k}->{$h};
- }
- }
- else # normal ref
- {
- my $xk = $x->{$k};
- if ($xk->can('copy'))
- {
- $self->{$k} = $xk->copy();
- }
- else
- {
- $self->{$k} = $xk->new($xk);
- }
- }
+ $self->{_a} = $_[1]->{_a} if defined $_[1]->{_a};
+ $self->{_p} = $_[1]->{_p} if defined $_[1]->{_p};
+ return $self;
}
+
+ my $self = bless {
+ sign => $_[0]->{sign},
+ value => $CALC->_copy($_[0]->{value}),
+ }, ref($_[0]);
+
+ $self->{_a} = $_[0]->{_a} if defined $_[0]->{_a};
+ $self->{_p} = $_[0]->{_p} if defined $_[0]->{_p};
$self;
}
if ((!ref $wanted) && ($wanted =~ /^([+-]?)[1-9][0-9]*\z/))
{
$self->{sign} = $1 || '+';
- my $ref = \$wanted;
+
if ($wanted =~ /^[+-]/)
{
# remove sign without touching wanted to make it work with constants
- my $t = $wanted; $t =~ s/^[+-]//; $ref = \$t;
+ my $t = $wanted; $t =~ s/^[+-]//;
+ $self->{value} = $CALC->_new($t);
+ }
+ else
+ {
+ $self->{value} = $CALC->_new($wanted);
}
- # force to string version (otherwise Pari is unhappy about overflowed
- # constants, for instance)
- # not good, BigInt shouldn't need to know about alternative libs:
- # $ref = \"$$ref" if $CALC eq 'Math::BigInt::Pari';
- $self->{value} = $CALC->_new($ref);
no strict 'refs';
if ( (defined $a) || (defined $p)
|| (defined ${"${class}::precision"})
}
# handle '+inf', '-inf' first
- if ($wanted =~ /^[+-]?inf$/)
+ if ($wanted =~ /^[+-]?inf\z/)
{
- $self->{value} = $CALC->_zero();
- $self->{sign} = $wanted; $self->{sign} = '+inf' if $self->{sign} eq 'inf';
- return $self;
+ $self->{sign} = $wanted; # set a default sign for bstr()
+ return $self->binf($wanted);
}
# split str in m mantissa, e exponent, i integer, f fraction, v value, s sign
- my ($mis,$miv,$mfv,$es,$ev) = _split(\$wanted);
+ my ($mis,$miv,$mfv,$es,$ev) = _split($wanted);
if (!ref $mis)
{
if ($_trap_nan)
}
}
$self->{sign} = '+' if $$miv eq '0'; # normalize -0 => +0
- $self->{value} = $CALC->_new($miv) if $self->{sign} =~ /^[+-]$/;
+ $self->{value} = $CALC->_new($$miv) if $self->{sign} =~ /^[+-]$/;
# if any of the globals is set, use them to round and store them inside $self
# do not round for new($x,undef,undef) since that is used by MBF to signal
# no rounding
}
$self->{sign} = $nan;
delete $self->{_a}; delete $self->{_p}; # rounding NaN is silly
- return $self;
+ $self;
}
sub binf
if (${"${class}::_trap_inf"})
{
require Carp;
- Carp::croak ("Tried to set $self to +-inf in $class\::binfn()");
+ Carp::croak ("Tried to set $self to +-inf in $class\::binf()");
}
$self->import() if $IMPORT == 0; # make require work
return if $self->modify('binf');
$sign = $sign . 'inf' if $sign !~ /inf$/; # - => -inf
$self->{sign} = $sign;
($self->{_a},$self->{_p}) = @_; # take over requested rounding
- return $self;
+ $self;
}
sub bzero
{
# create a bigint '+0', if given a BigInt, set it to 0
my $self = shift;
- $self = $class if !defined $self;
+ $self = __PACKAGE__ if !defined $self;
if (!ref($self))
{
sub bone
{
# create a bigint '+1' (or -1 if given sign '-'),
- # if given a BigInt, set it to +1 or -1, respecively
+ # if given a BigInt, set it to +1 or -1, respectively
my $self = shift;
my $sign = shift; $sign = '+' if !defined $sign || $sign ne '-';
$self = $class if !defined $self;
# (ref to BFLOAT or num_str ) return num_str
# Convert number from internal format to scientific string format.
# internal format is always normalized (no leading zeros, "-0E0" => "+0E0")
- my $x = shift; $class = ref($x) || $x; $x = $class->new(shift) if !ref($x);
- # my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
if ($x->{sign} !~ /^[+-]$/)
{
my ($m,$e) = $x->parts();
#$m->bstr() . 'e+' . $e->bstr(); # e can only be positive in BigInt
# 'e+' because E can only be positive in BigInt
- $m->bstr() . 'e+' . ${$CALC->_str($e->{value})};
+ $m->bstr() . 'e+' . $CALC->_str($e->{value});
}
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,@_);
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
if ($x->{sign} !~ /^[+-]$/)
{
return 'inf'; # +inf
}
my $es = ''; $es = $x->{sign} if $x->{sign} eq '-';
- $es.${$CALC->_str($x->{value})};
+ $es.$CALC->_str($x->{value});
}
sub numify
# $r round_mode, if given by caller
# @args all 'other' arguments (0 for unary, 1 for binary ops)
- # leave bigfloat parts alone
- return ($self) if exists $self->{_f} && ($self->{_f} & MB_NEVER_ROUND) != 0;
-
my $c = ref($self); # find out class of argument(s)
no strict 'refs';
+ # convert to normal scalar for speed and correctness in inner parts
+ $a = $a->can('numify') ? $a->numify() : "$a" if defined $a && ref($a);
+ $p = $p->can('numify') ? $p->numify() : "$p" if defined $p && ref($p);
+
# now pick $a or $p, but only if we have got "arguments"
if (!defined $a)
{
return ($self->bnan()) if defined $a && defined $p; # error
$r = ${"$c\::round_mode"} unless defined $r;
- if ($r !~ /^(even|odd|\+inf|\-inf|zero|trunc)$/)
+ if ($r !~ /^(even|odd|\+inf|\-inf|zero|trunc|common)$/)
{
require Carp; Carp::croak ("Unknown round mode '$r'");
}
# $r round_mode, if given by caller
# @args all 'other' arguments (0 for unary, 1 for binary ops)
- # leave bigfloat parts alone
- return ($self) if exists $self->{_f} && ($self->{_f} & MB_NEVER_ROUND) != 0;
-
my $c = ref($self); # find out class of argument(s)
no strict 'refs';
return $self->bnan() if defined $a && defined $p;
$r = ${"$c\::round_mode"} unless defined $r;
- if ($r !~ /^(even|odd|\+inf|\-inf|zero|trunc)$/)
+ if ($r !~ /^(even|odd|\+inf|\-inf|zero|trunc|common)$/)
{
require Carp; Carp::croak ("Unknown round mode '$r'");
}
{
$self->bfround($p,$r) if !defined $self->{_p} || $self->{_p} <= $p;
}
- $self->bnorm(); # after round, normalize
+ # bround() or bfround() already callled bnorm() if nec.
+ $self;
}
sub bnorm
{
# (BINT or num_str) return BINT
# make number absolute, or return absolute BINT from string
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
return $x if $x->modify('babs');
# post-normalized abs for internal use (does nothing for NaN)
{
# (BINT or num_str) return BINT
# negate number or make a negated number from string
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
return $x if $x->modify('bneg');
- # for +0 dont negate (to have always normalized)
- $x->{sign} =~ tr/+-/-+/ if !$x->is_zero(); # does nothing for NaN
+ # for +0 dont negate (to have always normalized +0). Does nothing for 'NaN'
+ $x->{sign} =~ tr/+-/-+/ unless ($x->{sign} eq '+' && $CALC->_is_zero($x->{value}));
$x;
}
# handle +-inf and NaN
return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
return 0 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} =~ /^[+-]inf$/;
- return +1; # inf is always bigger
+ return 1 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} !~ /^[+-]inf$/;
+ return -1;
}
$CALC->_acmp($x->{value},$y->{value}); # lib does only 0,1,-1
}
$x->{value} = $CALC->_sub($x->{value}, $y->{value}); # abs sub
}
}
- $x->round(@r) if !exists $x->{_f} || $x->{_f} & MB_NEVER_ROUND == 0;
- $x;
+ $x->round(@r);
}
sub bsub
# set up parameters
my ($self,$x,$y,@r) = (ref($_[0]),@_);
+
# objectify is costly, so avoid it
if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
{
return $x if $x->modify('bsub');
-# upgrade done by badd():
-# return $upgrade->badd($x,$y,@r) if defined $upgrade &&
-# ((!$x->isa($self)) || (!$y->isa($self)));
+ return $upgrade->new($x)->bsub($upgrade->new($y),@r) if defined $upgrade &&
+ ((!$x->isa($self)) || (!$y->isa($self)));
- if ($y->is_zero())
- {
- $x->round(@r) if !exists $x->{_f} || $x->{_f} & MB_NEVER_ROUND == 0;
- return $x;
- }
+ return $x->round(@r) if $y->is_zero();
+ # To correctly handle the lone special case $x->bsub($x), we note the sign
+ # of $x, then flip the sign from $y, and if the sign of $x did change, too,
+ # then we caught the special case:
+ my $xsign = $x->{sign};
$y->{sign} =~ tr/+\-/-+/; # does nothing for NaN
+ if ($xsign ne $x->{sign})
+ {
+ # special case of $x->bsub($x) results in 0
+ return $x->bzero(@r) if $xsign =~ /^[+-]$/;
+ return $x->bnan(); # NaN, -inf, +inf
+ }
$x->badd($y,@r); # badd does not leave internal zeros
$y->{sign} =~ tr/+\-/-+/; # refix $y (does nothing for NaN)
- $x; # already rounded by badd() or no round necc.
+ $x; # already rounded by badd() or no round nec.
}
sub binc
if ($x->{sign} eq '+')
{
$x->{value} = $CALC->_inc($x->{value});
- $x->round($a,$p,$r) if !exists $x->{_f} || $x->{_f} & MB_NEVER_ROUND == 0;
- return $x;
+ return $x->round($a,$p,$r);
}
elsif ($x->{sign} eq '-')
{
$x->{value} = $CALC->_dec($x->{value});
$x->{sign} = '+' if $CALC->_is_zero($x->{value}); # -1 +1 => -0 => +0
- $x->round($a,$p,$r) if !exists $x->{_f} || $x->{_f} & MB_NEVER_ROUND == 0;
- return $x;
+ return $x->round($a,$p,$r);
}
# inf, nan handling etc
$x->badd($self->bone(),$a,$p,$r); # badd does round
if ($x->{sign} eq '-')
{
- # < 0
+ # x already < 0
$x->{value} = $CALC->_inc($x->{value});
}
else
{
- return $x->badd($self->bone('-'),@r) unless $x->{sign} eq '+'; # inf/NaN
+ return $x->badd($self->bone('-'),@r) unless $x->{sign} eq '+'; # inf or NaN
# >= 0
if ($CALC->_is_zero($x->{value}))
{
$x->{value} = $CALC->_dec($x->{value});
}
}
- $x->round(@r) if !exists $x->{_f} || $x->{_f} & MB_NEVER_ROUND == 0;
- $x;
+ $x->round(@r);
}
sub blog
# $base of $x)
# set up parameters
- my ($self,$x,$base,@r) = (ref($_[0]),@_);
+ my ($self,$x,$base,@r) = (undef,@_);
# objectify is costly, so avoid it
if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
{
- ($self,$x,$base,@r) = objectify(2,$class,@_);
+ ($self,$x,$base,@r) = objectify(1,ref($x),@_);
}
+ return $x if $x->modify('blog');
+
+ $base = $self->new($base) if defined $base && !ref $base;
+
# inf, -inf, NaN, <0 => NaN
return $x->bnan()
- if $x->{sign} ne '+' || $base->{sign} ne '+';
-
+ if $x->{sign} ne '+' || (defined $base && $base->{sign} ne '+');
+
return $upgrade->blog($upgrade->new($x),$base,@r) if
- defined $upgrade && (ref($x) ne $upgrade || ref($base) ne $upgrade);
+ defined $upgrade;
- if ($CAN{log_int})
+ # fix for bug #24969:
+ # the default base is e (Euler's number) which is not an integer
+ if (!defined $base)
{
- my ($rc,$exact) = $CALC->_log_int($x->{value},$base->{value});
- return $x->bnan() unless defined $rc;
- $x->{value} = $rc;
- return $x->round(@r);
+ require Math::BigFloat;
+ my $u = Math::BigFloat->blog(Math::BigFloat->new($x))->as_int();
+ # modify $x in place
+ $x->{value} = $u->{value};
+ $x->{sign} = $u->{sign};
+ return $x;
+ }
+
+ my ($rc,$exact) = $CALC->_log_int($x->{value},$base->{value});
+ return $x->bnan() unless defined $rc; # not possible to take log?
+ $x->{value} = $rc;
+ $x->round(@r);
+ }
+
+sub bnok
+ {
+ # Calculate n over k (binomial coefficient or "choose" function) as integer.
+ # set up parameters
+ my ($self,$x,$y,@r) = (ref($_[0]),@_);
+
+ # objectify is costly, so avoid it
+ if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+ {
+ ($self,$x,$y,@r) = objectify(2,@_);
}
- require $EMU_LIB;
- __emu_blog($self,$x,$base,@r);
+ return $x if $x->modify('bnok');
+ return $x->bnan() if $x->{sign} eq 'NaN' || $y->{sign} eq 'NaN';
+ return $x->binf() if $x->{sign} eq '+inf';
+
+ # k > n or k < 0 => 0
+ my $cmp = $x->bacmp($y);
+ return $x->bzero() if $cmp < 0 || $y->{sign} =~ /^-/;
+ # k == n => 1
+ return $x->bone(@r) if $cmp == 0;
+
+ if ($CALC->can('_nok'))
+ {
+ $x->{value} = $CALC->_nok($x->{value},$y->{value});
+ }
+ else
+ {
+ # ( 7 ) 7! 7*6*5 * 4*3*2*1 7 * 6 * 5
+ # ( - ) = --------- = --------------- = ---------
+ # ( 3 ) 3! (7-3)! 3*2*1 * 4*3*2*1 3 * 2 * 1
+
+ # compute n - k + 2 (so we start with 5 in the example above)
+ my $z = $x - $y;
+ if (!$z->is_one())
+ {
+ $z->binc();
+ my $r = $z->copy(); $z->binc();
+ my $d = $self->new(2);
+ while ($z->bacmp($x) <= 0) # f < x ?
+ {
+ $r->bmul($z); $r->bdiv($d);
+ $z->binc(); $d->binc();
+ }
+ $x->{value} = $r->{value}; $x->{sign} = '+';
+ }
+ else { $x->bone(); }
+ }
+ $x->round(@r);
+ }
+
+sub bexp
+ {
+ # Calculate e ** $x (Euler's number to the power of X), truncated to
+ # an integer value.
+ my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
+ return $x if $x->modify('bexp');
+
+ # inf, -inf, NaN, <0 => NaN
+ return $x->bnan() if $x->{sign} eq 'NaN';
+ return $x->bone() if $x->is_zero();
+ return $x if $x->{sign} eq '+inf';
+ return $x->bzero() if $x->{sign} eq '-inf';
+
+ my $u;
+ {
+ # run through Math::BigFloat unless told otherwise
+ require Math::BigFloat unless defined $upgrade;
+ local $upgrade = 'Math::BigFloat' unless defined $upgrade;
+ # calculate result, truncate it to integer
+ $u = $upgrade->bexp($upgrade->new($x),@r);
+ }
+
+ if (!defined $upgrade)
+ {
+ $u = $u->as_int();
+ # modify $x in place
+ $x->{value} = $u->{value};
+ $x->round(@r);
+ }
+ else { $x = $u; }
}
sub blcm
{
$x = $class->new($y);
}
- while (@_) { $x = __lcm($x,shift); }
+ my $self = ref($x);
+ while (@_)
+ {
+ my $y = shift; $y = $self->new($y) if !ref ($y);
+ $x = __lcm($x,$y);
+ }
$x;
}
# GCD -- Euclids algorithm, variant C (Knuth Vol 3, pg 341 ff)
my $y = shift;
- $y = __PACKAGE__->new($y) if !ref($y);
+ $y = $class->new($y) if !ref($y);
my $self = ref($y);
- my $x = $y->copy(); # keep arguments
- if ($CAN{gcd})
- {
- while (@_)
- {
- $y = shift; $y = $self->new($y) if !ref($y);
- next if $y->is_zero();
- return $x->bnan() if $y->{sign} !~ /^[+-]$/; # y NaN?
- $x->{value} = $CALC->_gcd($x->{value},$y->{value}); last if $x->is_one();
- }
- }
- else
+ my $x = $y->copy()->babs(); # keep arguments
+ return $x->bnan() if $x->{sign} !~ /^[+-]$/; # x NaN?
+
+ while (@_)
{
- while (@_)
- {
- $y = shift; $y = $self->new($y) if !ref($y);
- $x = __gcd($x,$y->copy()); last if $x->is_one(); # _gcd handles NaN
- }
+ $y = shift; $y = $self->new($y) if !ref($y);
+ return $x->bnan() if $y->{sign} !~ /^[+-]$/; # y NaN?
+ $x->{value} = $CALC->_gcd($x->{value},$y->{value});
+ last if $CALC->_is_one($x->{value});
}
- $x->babs();
+ $x;
}
sub bnot
{
# return true when arg (BINT or num_str) is positive (>= 0)
my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
-
- $x->{sign} =~ /^\+/ ? 1 : 0; # +inf is also positive, but NaN not
+
+ return 1 if $x->{sign} eq '+inf'; # +inf is positive
+
+ # 0+ is neither positive nor negative
+ ($x->{sign} eq '+' && !$x->is_zero()) ? 1 : 0;
}
sub is_negative
# return true when arg (BINT or num_str) is negative (< 0)
my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
- $x->{sign} =~ /^-/ ? 1 : 0; # -inf is also negative, but NaN not
+ $x->{sign} =~ /^-/ ? 1 : 0; # -inf is negative, but NaN is not
}
sub is_int
sub bmul
{
- # multiply two numbers -- stolen from Knuth Vol 2 pg 233
+ # multiply the first number by the second numbers
# (BINT or num_str, BINT or num_str) return BINT
# set up parameters
{
($self,$x,$y,@r) = objectify(2,@_);
}
-
+
return $x if $x->modify('bmul');
return $x->bnan() if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
return $x->binf() if ($x->{sign} =~ /^-/ && $y->{sign} =~ /^-/);
return $x->binf('-');
}
-
- return $upgrade->bmul($x,$y,@r)
- if defined $upgrade && $y->isa($upgrade);
+
+ return $upgrade->bmul($x,$upgrade->new($y),@r)
+ if defined $upgrade && !$y->isa($self);
$r[3] = $y; # no push here
$x->{value} = $CALC->_mul($x->{value},$y->{value}); # do actual math
$x->{sign} = '+' if $CALC->_is_zero($x->{value}); # no -0
- $x->round(@r) if !exists $x->{_f} || $x->{_f} & MB_NEVER_ROUND == 0;
- $x;
+ $x->round(@r);
+ }
+
+sub bmuladd
+ {
+ # multiply two numbers and then add the third to the result
+ # (BINT or num_str, BINT or num_str, BINT or num_str) return BINT
+
+ # set up parameters
+ my ($self,$x,$y,$z,@r) = (ref($_[0]),@_);
+ # objectify is costly, so avoid it
+ if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+ {
+ ($self,$x,$y,$z,@r) = objectify(3,@_);
+ }
+
+ return $x if $x->modify('bmuladd');
+
+ return $x->bnan() if ($x->{sign} eq $nan) ||
+ ($y->{sign} eq $nan) ||
+ ($z->{sign} eq $nan);
+
+ # inf handling of x and y
+ if (($x->{sign} =~ /^[+-]inf$/) || ($y->{sign} =~ /^[+-]inf$/))
+ {
+ return $x->bnan() if $x->is_zero() || $y->is_zero();
+ # result will always be +-inf:
+ # +inf * +/+inf => +inf, -inf * -/-inf => +inf
+ # +inf * -/-inf => -inf, -inf * +/+inf => -inf
+ return $x->binf() if ($x->{sign} =~ /^\+/ && $y->{sign} =~ /^\+/);
+ return $x->binf() if ($x->{sign} =~ /^-/ && $y->{sign} =~ /^-/);
+ return $x->binf('-');
+ }
+ # inf handling x*y and z
+ if (($z->{sign} =~ /^[+-]inf$/))
+ {
+ # something +-inf => +-inf
+ $x->{sign} = $z->{sign}, return $x if $z->{sign} =~ /^[+-]inf$/;
+ }
+
+ return $upgrade->bmuladd($x,$upgrade->new($y),$upgrade->new($z),@r)
+ if defined $upgrade && (!$y->isa($self) || !$z->isa($self) || !$x->isa($self));
+
+ # TODO: what it $y and $z have A or P set?
+ $r[3] = $z; # no push here
+
+ $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-'; # +1 * +1 or -1 * -1 => +
+
+ $x->{value} = $CALC->_mul($x->{value},$y->{value}); # do actual math
+ $x->{sign} = '+' if $CALC->_is_zero($x->{value}); # no -0
+
+ my ($sx, $sz) = ( $x->{sign}, $z->{sign} ); # get signs
+
+ if ($sx eq $sz)
+ {
+ $x->{value} = $CALC->_add($x->{value},$z->{value}); # same sign, abs add
+ }
+ else
+ {
+ my $a = $CALC->_acmp ($z->{value},$x->{value}); # absolute compare
+ if ($a > 0)
+ {
+ $x->{value} = $CALC->_sub($z->{value},$x->{value},1); # abs sub w/ swap
+ $x->{sign} = $sz;
+ }
+ elsif ($a == 0)
+ {
+ # speedup, if equal, set result to 0
+ $x->{value} = $CALC->_zero();
+ $x->{sign} = '+';
+ }
+ else # a < 0
+ {
+ $x->{value} = $CALC->_sub($x->{value}, $z->{value}); # abs sub
+ }
+ }
+ $x->round(@r);
}
sub _div_inf
$x->{sign} = '+' if $CALC->_is_zero($x->{value});
$rem->{_a} = $x->{_a};
$rem->{_p} = $x->{_p};
- $x->round(@r) if !exists $x->{_f} || ($x->{_f} & MB_NEVER_ROUND) == 0;
+ $x->round(@r);
if (! $CALC->_is_zero($rem->{value}))
{
$rem->{sign} = $y->{sign};
{
$rem->{sign} = '+'; # dont leave -0
}
- $rem->round(@r) if !exists $rem->{_f} || ($rem->{_f} & MB_NEVER_ROUND) == 0;
+ $rem->round(@r);
return ($x,$rem);
}
$x->{value} = $CALC->_div($x->{value},$y->{value});
$x->{sign} = '+' if $CALC->_is_zero($x->{value});
- $x->round(@r) if !exists $x->{_f} || ($x->{_f} & MB_NEVER_ROUND) == 0;
- $x;
+ $x->round(@r);
}
###############################################################################
return $x->round(@r);
}
- if ($CAN{mod})
+ # calc new sign and in case $y == +/- 1, return $x
+ $x->{value} = $CALC->_mod($x->{value},$y->{value});
+ if (!$CALC->_is_zero($x->{value}))
{
- # calc new sign and in case $y == +/- 1, return $x
- $x->{value} = $CALC->_mod($x->{value},$y->{value});
- if (!$CALC->_is_zero($x->{value}))
- {
- my $xsign = $x->{sign};
- $x->{sign} = $y->{sign};
- if ($xsign ne $y->{sign})
- {
- my $t = $CALC->_copy($x->{value}); # copy $x
- $x->{value} = $CALC->_sub($y->{value},$t,1); # $y-$x
- }
- }
- else
- {
- $x->{sign} = '+'; # dont leave -0
- }
- $x->round(@r) if !exists $x->{_f} || $x->{_f} & MB_NEVER_ROUND == 0;
- return $x;
+ $x->{value} = $CALC->_sub($y->{value},$x->{value},1) # $y-$x
+ if ($x->{sign} ne $y->{sign});
+ $x->{sign} = $y->{sign};
}
- # disable upgrade temporarily, otherwise endless loop due to bdiv()
- local $upgrade = undef;
- my ($t,$rem) = $self->bdiv($x->copy(),$y,@r); # slow way (also rounds)
- # modify in place
- foreach (qw/value sign _a _p/)
+ else
{
- $x->{$_} = $rem->{$_};
+ $x->{sign} = '+'; # dont leave -0
}
- $x;
+ $x->round(@r);
}
sub bmodinv
# (i.e. their gcd is not one) then NaN is returned.
# set up parameters
- my ($self,$x,$y,@r) = (ref($_[0]),@_);
+ my ($self,$x,$y,@r) = (undef,@_);
# objectify is costly, so avoid it
if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
{
# put least residue into $x if $x was negative, and thus make it positive
$x->bmod($y) if $x->{sign} eq '-';
- if ($CAN{modinv})
- {
- my $sign;
- ($x->{value},$sign) = $CALC->_modinv($x->{value},$y->{value});
- return $x->bnan() if !defined $x->{value}; # in case no GCD found
- return $x if !defined $sign; # already real result
- $x->{sign} = $sign; # flip/flop see below
- $x->bmod($y); # calc real result
- return $x;
- }
-
- require $EMU_LIB;
- __emu_bmodinv($self,$x,$y,@r);
+ my $sign;
+ ($x->{value},$sign) = $CALC->_modinv($x->{value},$y->{value});
+ return $x->bnan() if !defined $x->{value}; # in case no GCD found
+ return $x if !defined $sign; # already real result
+ $x->{sign} = $sign; # flip/flop see below
+ $x->bmod($y); # calc real result
+ $x;
}
sub bmodpow
{
# takes a very large number to a very large exponent in a given very
- # large modulus, quickly, thanks to binary exponentation. supports
+ # large modulus, quickly, thanks to binary exponentation. Supports
# negative exponents.
my ($self,$num,$exp,$mod,@r) = objectify(3,@_);
# check num for valid values (also NaN if there was no inverse but $exp < 0)
return $num->bnan() if $num->{sign} !~ /^[+-]$/;
- if ($CAN{modpow})
- {
- # $mod is positive, sign on $exp is ignored, result also positive
- $num->{value} = $CALC->_modpow($num->{value},$exp->{value},$mod->{value});
- return $num;
- }
-
- require $EMU_LIB;
- __emu_bmodpow($self,$num,$exp,$mod,@r);
+ # $mod is positive, sign on $exp is ignored, result also positive
+ $num->{value} = $CALC->_modpow($num->{value},$exp->{value},$mod->{value});
+ $num;
}
###############################################################################
{
# (BINT or num_str, BINT or num_str) return BINT
# compute factorial number from $x, modify $x in place
- my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
-
- return $x if $x->modify('bfac');
-
- return $x if $x->{sign} eq '+inf'; # inf => inf
- return $x->bnan() if $x->{sign} ne '+'; # NaN, <0 etc => NaN
+ my ($self,$x,@r) = ref($_[0]) ? (undef,@_) : objectify(1,@_);
- if ($CAN{fac})
- {
- $x->{value} = $CALC->_fac($x->{value});
- return $x->round(@r);
- }
+ return $x if $x->modify('bfac') || $x->{sign} eq '+inf'; # inf => inf
+ return $x->bnan() if $x->{sign} ne '+'; # NaN, <0 etc => NaN
- require $EMU_LIB;
- __emu_bfac($self,$x,@r);
+ $x->{value} = $CALC->_fac($x->{value});
+ $x->round(@r);
}
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);
+ if defined $upgrade && (!$y->isa($self) || $y->{sign} eq '-');
$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
- if ($x->{sign} eq '-' && $CALC->_is_one($x->{value}))
- {
- # if $x == -1 and odd/even y => +1/-1
- return $y->is_odd() ? $x->round(@r) : $x->babs()->round(@r);
- # my Casio FX-5500L has a bug here: -1 ** 2 is -1, but -1 * -1 is 1;
- }
+ my $new_sign = '+';
+ $new_sign = $y->is_odd() ? '-' : '+' if ($x->{sign} ne '+');
+
+ # 0 ** -7 => ( 1 / (0 ** 7)) => 1 / 0 => +inf
+ return $x->binf()
+ if $y->{sign} eq '-' && $x->{sign} eq '+' && $CALC->_is_zero($x->{value});
# 1 ** -y => 1 / (1 ** |y|)
# so do test for negative $y after above's clause
- return $x->bnan() if $y->{sign} eq '-' && !$x->is_one();
+ return $x->bnan() if $y->{sign} eq '-' && !$CALC->_is_one($x->{value});
- if ($CAN{pow})
- {
- $x->{value} = $CALC->_pow($x->{value},$y->{value});
- $x->{sign} = '+' if $CALC->_is_zero($y->{value});
- $x->round(@r) if !exists $x->{_f} || $x->{_f} & MB_NEVER_ROUND == 0;
- return $x;
- }
-
- require $EMU_LIB;
- __emu_bpow($self,$x,$y,@r);
+ $x->{value} = $CALC->_pow($x->{value},$y->{value});
+ $x->{sign} = $new_sign;
+ $x->{sign} = '+' if $CALC->_is_zero($y->{value});
+ $x->round(@r);
}
sub blsft
$n = 2 if !defined $n; return $x->bnan() if $n <= 0 || $y->{sign} eq '-';
- my $t; $t = $CALC->_lsft($x->{value},$y->{value},$n) if $CAN{lsft};
- if (defined $t)
- {
- $x->{value} = $t; return $x->round(@r);
- }
- # fallback
- $x->bmul( $self->bpow($n, $y, @r), @r );
+ $x->{value} = $CALC->_lsft($x->{value},$y->{value},$n);
+ $x->round(@r);
}
sub brsft
$bin =~ s/^-0b//; # strip '-0b' prefix
$bin =~ tr/10/01/; # flip bits
# now shift
- if (CORE::length($bin) <= $y)
+ if ($y >= CORE::length($bin))
{
$bin = '0'; # shifting to far right creates -1
# 0, because later increment makes
$x->bdec(); # n == 2, but $y == 1: this fixes it
}
- my $t; $t = $CALC->_rsft($x->{value},$y->{value},$n) if $CAN{rsft};
- if (defined $t)
- {
- $x->{value} = $t;
- return $x->round(@r);
- }
- # fallback
- $x->bdiv($self->bpow($n,$y, @r), @r);
- $x;
+ $x->{value} = $CALC->_rsft($x->{value},$y->{value},$n);
+ $x->round(@r);
}
sub band
my $sx = $x->{sign} eq '+' ? 1 : -1;
my $sy = $y->{sign} eq '+' ? 1 : -1;
- if ($CAN{and} && $sx == 1 && $sy == 1)
+ if ($sx == 1 && $sy == 1)
{
$x->{value} = $CALC->_and($x->{value},$y->{value});
return $x->round(@r);
return $x if $x->modify('bior');
$r[3] = $y; # no push!
- local $Math::BigInt::upgrade = undef;
-
return $x->bnan() if ($x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/);
my $sx = $x->{sign} eq '+' ? 1 : -1;
# the sign of X follows the sign of X, e.g. sign of Y irrelevant for bior()
# don't use lib for negative values
- if ($CAN{or} && $sx == 1 && $sy == 1)
+ if ($sx == 1 && $sy == 1)
{
$x->{value} = $CALC->_or($x->{value},$y->{value});
return $x->round(@r);
my $sy = $y->{sign} eq '+' ? 1 : -1;
# don't use lib for negative values
- if ($CAN{xor} && $sx == 1 && $sy == 1)
+ if ($sx == 1 && $sy == 1)
{
$x->{value} = $CALC->_xor($x->{value},$y->{value});
return $x->round(@r);
sub digit
{
# return the nth decimal digit, negative values count backward, 0 is right
- my ($self,$x,$n) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
+ my ($self,$x,$n) = ref($_[0]) ? (undef,@_) : objectify(1,@_);
+ $n = $n->numify() if ref($n);
$CALC->_digit($x->{value},$n||0);
}
my $x = shift;
$x = $class->new($x) unless ref $x;
- return 0 if $x->is_zero() || $x->is_odd() || $x->{sign} !~ /^[+-]$/;
-
- return $CALC->_zeros($x->{value}) if $CAN{zeros};
+ return 0 if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf etc
- # if not: since we do not know underlying internal representation:
- my $es = "$x"; $es =~ /([0]*)$/;
- return 0 if !defined $1; # no zeros
- CORE::length("$1"); # as string, not as +0!
+ $CALC->_zeros($x->{value}); # must handle odd values, 0 etc
}
sub bsqrt
{
# calculate square root of $x
- my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
+ my ($self,$x,@r) = ref($_[0]) ? (undef,@_) : objectify(1,@_);
return $x if $x->modify('bsqrt');
return $upgrade->bsqrt($x,@r) if defined $upgrade;
- if ($CAN{sqrt})
- {
- $x->{value} = $CALC->_sqrt($x->{value});
- return $x->round(@r);
- }
-
- require $EMU_LIB;
- __emu_bsqrt($self,$x,@r);
+ $x->{value} = $CALC->_sqrt($x->{value});
+ $x->round(@r);
}
sub broot
# objectify is costly, so avoid it
if ((!ref($x)) || (ref($x) ne ref($y)))
{
- ($self,$x,$y,@r) = $self->objectify(2,@_);
+ ($self,$x,$y,@r) = objectify(2,$self || $class,@_);
}
return $x if $x->modify('broot');
return $upgrade->new($x)->broot($upgrade->new($y),@r) if defined $upgrade;
- if ($CAN{root})
- {
- $x->{value} = $CALC->_root($x->{value},$y->{value});
- return $x->round(@r);
- }
-
- require $EMU_LIB;
- __emu_broot($self,$x,$y,@r);
+ $x->{value} = $CALC->_root($x->{value},$y->{value});
+ $x->round(@r);
}
sub exponent
}
return $self->bone() if $x->is_zero();
- $self->new($x->_trailing_zeros());
+ # 12300 => 2 trailing zeros => exponent is 2
+ $self->new( $CALC->_zeros($x->{value}) );
}
sub mantissa
return $self->new($x->{sign});
}
my $m = $x->copy(); delete $m->{_p}; delete $m->{_a};
+
# that's a bit inefficient:
- my $zeros = $m->_trailing_zeros();
+ my $zeros = $CALC->_zeros($m->{value});
$m->brsft($zeros,10) if $zeros != 0;
$m;
}
{
# precision: round to the $Nth digit left (+$n) or right (-$n) from the '.'
# $n == 0 || $n == 1 => round to integer
- my $x = shift; $x = $class->new($x) unless ref $x;
+ my $x = shift; my $self = ref($x) || $x; $x = $self->new($x) unless ref $x;
- my ($scale,$mode) = $x->_scale_p($x->precision(),$x->round_mode(),@_);
+ my ($scale,$mode) = $x->_scale_p(@_);
return $x if !defined $scale || $x->modify('bfround'); # no-op
# no-op for BigInts if $n <= 0
$x->bround( $x->length()-$scale, $mode) if $scale > 0;
- $x->{_a} = undef; # bround sets {_a}
- $x->{_p} = $scale; # so correct it
+ delete $x->{_a}; # delete to save memory
+ $x->{_p} = $scale; # store new _p
$x;
}
sub _scan_for_nonzero
{
- # internal, used by bround()
- my ($x,$pad,$xs) = @_;
+ # internal, used by bround() to scan for non-zeros after a '5'
+ my ($x,$pad,$xs,$len) = @_;
- my $len = $x->length();
- return 0 if $len == 1; # '5' is trailed by invisible zeros
+ return 0 if $len == 1; # "5" is trailed by invisible zeros
my $follow = $pad - 1;
return 0 if $follow > $len || $follow < 1;
- # since we do not know underlying represention of $x, use decimal string
- my $r = substr ("$x",-$follow);
- $r =~ /[^0]/ ? 1 : 0;
+ # use the string form to check whether only '0's follow or not
+ substr ($xs,-$follow) =~ /[^0]/ ? 1 : 0;
}
sub fround
{
# Exists to make life easier for switch between MBF and MBI (should we
# autoload fxxx() like MBF does for bxxx()?)
- my $x = shift;
+ my $x = shift; $x = $class->new($x) unless ref $x;
$x->bround(@_);
}
# do not return $x->bnorm(), but $x
my $x = shift; $x = $class->new($x) unless ref $x;
- my ($scale,$mode) = $x->_scale_a($x->accuracy(),$x->round_mode(),@_);
- return $x if !defined $scale; # no-op
- return $x if $x->modify('bround');
+ my ($scale,$mode) = $x->_scale_a(@_);
+ return $x if !defined $scale || $x->modify('bround'); # no-op
if ($x->is_zero() || $scale == 0)
{
$pad = $len - $scale;
$pad = abs($scale-1) if $scale < 0;
- # do not use digit(), it is costly for binary => decimal
-
+ # do not use digit(), it is very costly for binary => decimal
+ # getting the entire string is also costly, but we need to do it only once
my $xs = $CALC->_str($x->{value});
my $pl = -$pad-1;
# pad: 123: 0 => -1, at 1 => -2, at 2 => -3, at 3 => -4
# pad+1: 123: 0 => 0, at 1 => -1, at 2 => -2, at 3 => -3
- $digit_round = '0'; $digit_round = substr($$xs,$pl,1) if $pad <= $len;
+ $digit_round = '0'; $digit_round = substr($xs,$pl,1) if $pad <= $len;
$pl++; $pl ++ if $pad >= $len;
- $digit_after = '0'; $digit_after = substr($$xs,$pl,1) if $pad > 0;
+ $digit_after = '0'; $digit_after = substr($xs,$pl,1) if $pad > 0;
# in case of 01234 we round down, for 6789 up, and only in case 5 we look
# closer at the remaining digits of the original $x, remember decision
($digit_after =~ /[01234]/) || # round down anyway,
# 6789 => round up
($digit_after eq '5') && # not 5000...0000
- ($x->_scan_for_nonzero($pad,$xs) == 0) &&
+ ($x->_scan_for_nonzero($pad,$xs,$len) == 0) &&
(
($mode eq 'even') && ($digit_round =~ /[24680]/) ||
($mode eq 'odd') && ($digit_round =~ /[13579]/) ||
if (($pad > 0) && ($pad <= $len))
{
- substr($$xs,-$pad,$pad) = '0' x $pad;
- $put_back = 1;
+ substr($xs,-$pad,$pad) = '0' x $pad; # replace with '00...'
+ $put_back = 1; # need to put back
}
elsif ($pad > $len)
{
if ($round_up) # what gave test above?
{
- $put_back = 1;
- $pad = $len, $$xs = '0' x $pad if $scale < 0; # tlr: whack 0.51=>1.0
+ $put_back = 1; # need to put back
+ $pad = $len, $xs = '0' x $pad if $scale < 0; # tlr: whack 0.51=>1.0
# we modify directly the string variant instead of creating a number and
# adding it, since that is faster (we already have the string)
my $c = 0; $pad ++; # for $pad == $len case
while ($pad <= $len)
{
- $c = substr($$xs,-$pad,1) + 1; $c = '0' if $c eq '10';
- substr($$xs,-$pad,1) = $c; $pad++;
+ $c = substr($xs,-$pad,1) + 1; $c = '0' if $c eq '10';
+ substr($xs,-$pad,1) = $c; $pad++;
last if $c != 0; # no overflow => early out
}
- $$xs = '1'.$$xs if $c == 0;
+ $xs = '1'.$xs if $c == 0;
}
- $x->{value} = $CALC->_new($xs) if $put_back == 1; # put back in if needed
+ $x->{value} = $CALC->_new($xs) if $put_back == 1; # put back, if needed
$x->{_a} = $scale if $scale >= 0;
if ($scale < 0)
sub as_number
{
# An object might be asked to return itself as bigint on certain overloaded
- # operations, this does exactly this, so that sub classes can simple inherit
+ # operations. This does exactly this, so that sub classes can simple inherit
# it or override with their own integer conversion routine.
$_[0]->copy();
}
my $s = '';
$s = $x->{sign} if $x->{sign} eq '-';
- if ($CAN{as_hex})
- {
- return $s . ${$CALC->_as_hex($x->{value})};
- }
-
- require $EMU_LIB;
- __emu_as_hex(ref($x),$x,$s);
+ $s . $CALC->_as_hex($x->{value});
}
sub as_bin
return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, nan etc
my $s = ''; $s = $x->{sign} if $x->{sign} eq '-';
- if ($CAN{as_bin})
- {
- return $s . ${$CALC->_as_bin($x->{value})};
- }
+ return $s . $CALC->_as_bin($x->{value});
+ }
- require $EMU_LIB;
- __emu_as_bin(ref($x),$x,$s);
+sub as_oct
+ {
+ # return as octal string, with prefixed 0
+ my $x = shift; $x = $class->new($x) if !ref($x);
+
+ return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, nan etc
+ my $s = ''; $s = $x->{sign} if $x->{sign} eq '-';
+ return $s . $CALC->_as_oct($x->{value});
}
##############################################################################
# the first argument is number of args objectify() should look at it will
# return $count+1 elements, the first will be a classname. This is because
# overloaded '""' calls bstr($object,undef,undef) and this would result in
- # useless objects beeing created and thrown away. So we cannot simple loop
+ # useless objects being created and thrown away. So we cannot simple loop
# over @_. If the given count is 0, all arguments will be used.
# If the second arg is a ref, use it as class.
}
my $up = ${"$a[0]::upgrade"};
- #print "Now in objectify, my class is today $a[0], count = $count\n";
+ # print STDERR "# Now in objectify, my class is today $a[0], count = $count\n";
if ($count == 0)
{
while (@_)
while ($count > 0)
{
$count--;
- $k = shift;
+ $k = shift;
if (!ref($k))
{
$k = $a[0]->new($k);
elsif (!defined $up && ref($k) ne $a[0])
{
# foreign object, try to convert to integer
- $k->can('as_number') ? $k = $k->as_number() : $k = $a[0]->new($k);
+ $k->can('as_number') ? $k = $k->as_number() : $k = $a[0]->new($k);
}
push @a,$k;
}
@a;
}
+sub _register_callback
+ {
+ my ($class,$callback) = @_;
+
+ if (ref($callback) ne 'CODE')
+ {
+ require Carp;
+ Carp::croak ("$callback is not a coderef");
+ }
+ $CALLBACKS{$class} = $callback;
+ }
+
sub import
{
my $self = shift;
$IMPORT++; # remember we did import()
my @a; my $l = scalar @_;
+ my $warn_or_die = 0; # 0 - no warn, 1 - warn, 2 - die
for ( my $i = 0; $i < $l ; $i++ )
{
if ($_[$i] eq ':constant')
$upgrade = $_[$i+1]; # or undef to disable
$i++;
}
- elsif ($_[$i] =~ /^lib$/i)
+ elsif ($_[$i] =~ /^(lib|try|only)\z/)
{
# this causes a different low lib to take care...
$CALC = $_[$i+1] || '';
+ # lib => 1 (warn on fallback), try => 0 (no warn), only => 2 (die on fallback)
+ $warn_or_die = 1 if $_[$i] eq 'lib';
+ $warn_or_die = 2 if $_[$i] eq 'only';
$i++;
}
else
}
}
# any non :constant stuff is handled by our parent, Exporter
- # even if @_ is empty, to give it a chance
- $self->SUPER::import(@a); # need it for subclasses
- $self->export_to_level(1,$self,@a); # need it for MBF
+ if (@a > 0)
+ {
+ require Exporter;
+
+ $self->SUPER::import(@a); # need it for subclasses
+ $self->export_to_level(1,$self,@a); # need it for MBF
+ }
# try to load core math lib
my @c = split /\s*,\s*/,$CALC;
- push @c,'Calc'; # if all fail, try this
+ foreach (@c)
+ {
+ $_ =~ tr/a-zA-Z0-9://cd; # limit to sane characters
+ }
+ push @c, \'FastCalc', \'Calc' # if all fail, try these
+ if $warn_or_die < 2; # but not for "only"
$CALC = ''; # signal error
- foreach my $lib (@c)
+ foreach my $l (@c)
{
+ # fallback libraries are "marked" as \'string', extract string if nec.
+ my $lib = $l; $lib = $$l if ref($l);
+
next if ($lib || '') eq '';
$lib = 'Math::BigInt::'.$lib if $lib !~ /^Math::BigInt/i;
$lib =~ s/\.pm$//;
if ($] < 5.006)
{
- # Perl < 5.6.0 dies with "out of memory!" when eval() and ':constant' is
- # used in the same script, or eval inside import().
+ # Perl < 5.6.0 dies with "out of memory!" when eval("") and ':constant' is
+ # used in the same script, or eval("") inside import().
my @parts = split /::/, $lib; # Math::BigInt => Math BigInt
my $file = pop @parts; $file .= '.pm'; # BigInt => BigInt.pm
require File::Spec;
{
eval "use $lib qw/@c/;";
}
- $CALC = $lib, last if $@ eq ''; # no error in loading lib?
+ if ($@ eq '')
+ {
+ my $ok = 1;
+ # loaded it ok, see if the api_version() is high enough
+ if ($lib->can('api_version') && $lib->api_version() >= 1.0)
+ {
+ $ok = 0;
+ # api_version matches, check if it really provides anything we need
+ for my $method (qw/
+ one two ten
+ str num
+ add mul div sub dec inc
+ acmp len digit is_one is_zero is_even is_odd
+ is_two is_ten
+ zeros new copy check
+ from_hex from_oct from_bin as_hex as_bin as_oct
+ rsft lsft xor and or
+ mod sqrt root fac pow modinv modpow log_int gcd
+ /)
+ {
+ if (!$lib->can("_$method"))
+ {
+ if (($WARN{$lib}||0) < 2)
+ {
+ require Carp;
+ Carp::carp ("$lib is missing method '_$method'");
+ $WARN{$lib} = 1; # still warn about the lib
+ }
+ $ok++; last;
+ }
+ }
+ }
+ if ($ok == 0)
+ {
+ $CALC = $lib;
+ if ($warn_or_die > 0 && ref($l))
+ {
+ require Carp;
+ my $msg = "Math::BigInt: couldn't load specified math lib(s), fallback to $lib";
+ Carp::carp ($msg) if $warn_or_die == 1;
+ Carp::croak ($msg) if $warn_or_die == 2;
+ }
+ last; # found a usable one, break
+ }
+ else
+ {
+ if (($WARN{$lib}||0) < 2)
+ {
+ my $ver = eval "\$$lib\::VERSION" || 'unknown';
+ require Carp;
+ Carp::carp ("Cannot load outdated $lib v$ver, please upgrade");
+ $WARN{$lib} = 2; # never warn again
+ }
+ }
+ }
}
if ($CALC eq '')
{
require Carp;
- Carp::croak ("Couldn't load any math lib, not even 'Calc.pm'");
+ if ($warn_or_die == 2)
+ {
+ Carp::croak ("Couldn't load specified math lib(s) and fallback disallowed");
+ }
+ else
+ {
+ Carp::croak ("Couldn't load any math lib(s), not even fallback to Calc.pm");
+ }
}
- _fill_can_cache();
- }
-sub _fill_can_cache
- {
- # fill $CAN with the results of $CALC->can(...)
+ # notify callbacks
+ foreach my $class (keys %CALLBACKS)
+ {
+ &{$CALLBACKS{$class}}($CALC);
+ }
+
+ # Fill $CAN with the results of $CALC->can(...) for emulating lower math lib
+ # functions
%CAN = ();
- for my $method (qw/gcd mod modinv modpow fac pow lsft rsft
- and signed_and or signed_or xor signed_xor
- from_hex as_hex from_bin as_bin
- zeros sqrt root log_int log
- /)
+ for my $method (qw/ signed_and signed_or signed_xor /)
{
$CAN{$method} = $CALC->can("_$method") ? 1 : 0;
}
+
+ # import done
+ }
+
+sub from_hex
+ {
+ # create a bigint from a hexadecimal string
+ my ($self, $hs) = @_;
+
+ my $rc = $self->__from_hex($hs);
+
+ return $self->bnan() unless defined $rc;
+
+ $rc;
+ }
+
+sub from_bin
+ {
+ # create a bigint from a hexadecimal string
+ my ($self, $bs) = @_;
+
+ my $rc = $self->__from_bin($bs);
+
+ return $self->bnan() unless defined $rc;
+
+ $rc;
+ }
+
+sub from_oct
+ {
+ # create a bigint from a hexadecimal string
+ my ($self, $os) = @_;
+
+ my $x = $self->bzero();
+
+ # strip underscores
+ $os =~ s/([0-9a-fA-F])_([0-9a-fA-F])/$1$2/g;
+ $os =~ s/([0-9a-fA-F])_([0-9a-fA-F])/$1$2/g;
+
+ return $x->bnan() if $os !~ /^[\-\+]?0[0-9]+$/;
+
+ my $sign = '+'; $sign = '-' if $os =~ /^-/;
+
+ $os =~ s/^[+-]//; # strip sign
+ $x->{value} = $CALC->_from_oct($os);
+ $x->{sign} = $sign unless $CALC->_is_zero($x->{value}); # no '-0'
+ $x;
}
sub __from_hex
{
+ # internal
# convert a (ref to) big hex string to BigInt, return undef for error
my $hs = shift;
my $x = Math::BigInt->bzero();
# strip underscores
- $$hs =~ s/([0-9a-fA-F])_([0-9a-fA-F])/$1$2/g;
- $$hs =~ s/([0-9a-fA-F])_([0-9a-fA-F])/$1$2/g;
+ $hs =~ s/([0-9a-fA-F])_([0-9a-fA-F])/$1$2/g;
+ $hs =~ s/([0-9a-fA-F])_([0-9a-fA-F])/$1$2/g;
- return $x->bnan() if $$hs !~ /^[\-\+]?0x[0-9A-Fa-f]+$/;
+ return $x->bnan() if $hs !~ /^[\-\+]?0x[0-9A-Fa-f]+$/;
- my $sign = '+'; $sign = '-' if ($$hs =~ /^-/);
+ my $sign = '+'; $sign = '-' if $hs =~ /^-/;
- $$hs =~ s/^[+-]//; # strip sign
- if ($CAN{'from_hex'})
- {
- $x->{value} = $CALC->_from_hex($hs);
- }
- else
- {
- # fallback to pure perl
- my $mul = Math::BigInt->bone();
- my $x65536 = Math::BigInt->new(65536);
- my $len = CORE::length($$hs)-2; # minus 2 for 0x
- $len = int($len/4); # 4-digit parts, w/o '0x'
- my $val; my $i = -4;
- while ($len >= 0)
- {
- $val = substr($$hs,$i,4);
- $val =~ s/^[+-]?0x// if $len == 0; # for last part only because
- $val = hex($val); # hex does not like wrong chars
- $i -= 4; $len --;
- $x += $mul * $val if $val != 0;
- $mul *= $x65536 if $len >= 0; # skip last mul
- }
- }
+ $hs =~ s/^[+-]//; # strip sign
+ $x->{value} = $CALC->_from_hex($hs);
$x->{sign} = $sign unless $CALC->_is_zero($x->{value}); # no '-0'
$x;
}
sub __from_bin
{
+ # internal
# convert a (ref to) big binary string to BigInt, return undef for error
my $bs = shift;
my $x = Math::BigInt->bzero();
+
# strip underscores
- $$bs =~ s/([01])_([01])/$1$2/g;
- $$bs =~ s/([01])_([01])/$1$2/g;
- return $x->bnan() if $$bs !~ /^[+-]?0b[01]+$/;
+ $bs =~ s/([01])_([01])/$1$2/g;
+ $bs =~ s/([01])_([01])/$1$2/g;
+ return $x->bnan() if $bs !~ /^[+-]?0b[01]+$/;
- my $sign = '+'; $sign = '-' if ($$bs =~ /^\-/);
- $$bs =~ s/^[+-]//; # strip sign
- if ($CAN{'from_bin'})
- {
- $x->{value} = $CALC->_from_bin($bs);
- }
- else
- {
- my $mul = Math::BigInt->bone();
- my $x256 = Math::BigInt->new(256);
- my $len = CORE::length($$bs)-2; # minus 2 for 0b
- $len = int($len/8); # 8-digit parts, w/o '0b'
- my $val; my $i = -8;
- while ($len >= 0)
- {
- $val = substr($$bs,$i,8);
- $val =~ s/^[+-]?0b// if $len == 0; # for last part only
- #$val = oct('0b'.$val); # does not work on Perl prior to 5.6.0
- # slower:
- # $val = ('0' x (8-CORE::length($val))).$val if CORE::length($val) < 8;
- $val = ord(pack('B8',substr('00000000'.$val,-8,8)));
- $i -= 8; $len --;
- $x += $mul * $val if $val != 0;
- $mul *= $x256 if $len >= 0; # skip last mul
- }
- }
+ my $sign = '+'; $sign = '-' if $bs =~ /^\-/;
+ $bs =~ s/^[+-]//; # strip sign
+
+ $x->{value} = $CALC->_from_bin($bs);
$x->{sign} = $sign unless $CALC->_is_zero($x->{value}); # no '-0'
$x;
}
sub _split
{
- # (ref to num_str) return num_str
- # internal, take apart a string and return the pieces
- # strip leading/trailing whitespace, leading zeros, underscore and reject
- # invalid input
+ # input: num_str; output: undef for invalid or
+ # (\$mantissa_sign,\$mantissa_value,\$mantissa_fraction,\$exp_sign,\$exp_value)
+ # Internal, take apart a string and return the pieces.
+ # Strip leading/trailing whitespace, leading zeros, underscore and reject
+ # invalid input.
my $x = shift;
# strip white space at front, also extranous leading zeros
- $$x =~ s/^\s*([-]?)0*([0-9])/$1$2/g; # will not strip ' .2'
- $$x =~ s/^\s+//; # but this will
- $$x =~ s/\s+$//g; # strip white space at end
+ $x =~ s/^\s*([-]?)0*([0-9])/$1$2/g; # will not strip ' .2'
+ $x =~ s/^\s+//; # but this will
+ $x =~ s/\s+$//g; # strip white space at end
# shortcut, if nothing to split, return early
- if ($$x =~ /^[+-]?\d+\z/)
+ if ($x =~ /^[+-]?[0-9]+\z/)
{
- $$x =~ s/^([+-])0*([0-9])/$2/; my $sign = $1 || '+';
- return (\$sign, $x, \'', \'', \0);
+ $x =~ s/^([+-])0*([0-9])/$2/; my $sign = $1 || '+';
+ return (\$sign, \$x, \'', \'', \0);
}
# invalid starting char?
- return if $$x !~ /^[+-]?(\.?[0-9]|0b[0-1]|0x[0-9a-fA-F])/;
+ return if $x !~ /^[+-]?(\.?[0-9]|0b[0-1]|0x[0-9a-fA-F])/;
- return __from_hex($x) if $$x =~ /^[\-\+]?0x/; # hex string
- return __from_bin($x) if $$x =~ /^[\-\+]?0b/; # binary string
+ return __from_hex($x) if $x =~ /^[\-\+]?0x/; # hex string
+ return __from_bin($x) if $x =~ /^[\-\+]?0b/; # binary string
# strip underscores between digits
- $$x =~ s/(\d)_(\d)/$1$2/g;
- $$x =~ s/(\d)_(\d)/$1$2/g; # do twice for 1_2_3
+ $x =~ s/([0-9])_([0-9])/$1$2/g;
+ $x =~ s/([0-9])_([0-9])/$1$2/g; # do twice for 1_2_3
# some possible inputs:
# 2.1234 # 0.12 # 1 # 1E1 # 2.134E1 # 434E-10 # 1.02009E-2
# .2 # 1_2_3.4_5_6 # 1.4E1_2_3 # 1e3 # +.2 # 0e999
- #return if $$x =~ /[Ee].*[Ee]/; # more than one E => error
-
- my ($m,$e,$last) = split /[Ee]/,$$x;
+ my ($m,$e,$last) = split /[Ee]/,$x;
return if defined $last; # last defined => 1e2E3 or others
$e = '0' if !defined $e || $e eq "";
# sign,value for exponent,mantint,mantfrac
my ($es,$ev,$mis,$miv,$mfv);
# valid exponent?
- if ($e =~ /^([+-]?)0*(\d+)$/) # strip leading zeros
+ if ($e =~ /^([+-]?)0*([0-9]+)$/) # strip leading zeros
{
$es = $1; $ev = $2;
# valid mantissa?
$mi = '0' if !defined $mi;
$mi .= '0' if $mi =~ /^[\-\+]?$/;
$mf = '0' if !defined $mf || $mf eq '';
- if ($mi =~ /^([+-]?)0*(\d+)$/) # strip leading zeros
+ if ($mi =~ /^([+-]?)0*([0-9]+)$/) # strip leading zeros
{
$mis = $1||'+'; $miv = $2;
- return unless ($mf =~ /^(\d*?)0*$/); # strip trailing zeros
+ return unless ($mf =~ /^([0-9]*?)0*$/); # strip trailing zeros
$mfv = $1;
# handle the 0e999 case here
$ev = 0 if $miv eq '0' && $mfv eq '';
# does modify first argument
# LCM
- my $x = shift; my $ty = shift;
+ my ($x,$ty) = @_;
return $x->bnan() if ($x->{sign} eq $nan) || ($ty->{sign} eq $nan);
- return $x * $ty / bgcd($x,$ty);
+ my $method = ref($x) . '::bgcd';
+ no strict 'refs';
+ $x * $ty / &$method($x,$ty);
}
-sub __gcd
- {
- # (BINT or num_str, BINT or num_str) return BINT
- # does modify both arguments
- # GCD -- Euclids algorithm E, Knuth Vol 2 pg 296
- my ($x,$ty) = @_;
-
- return $x->bnan() if $x->{sign} !~ /^[+-]$/ || $ty->{sign} !~ /^[+-]$/;
+###############################################################################
+# trigonometric functions
- while (!$ty->is_zero())
+sub bpi
+ {
+ # Calculate PI to N digits. Unless upgrading is in effect, returns the
+ # result truncated to an integer, that is, always returns '3'.
+ my ($self,$n) = @_;
+ if (@_ == 1)
{
- ($x, $ty) = ($ty,bmod($x,$ty));
+ # called like Math::BigInt::bpi(10);
+ $n = $self; $self = $class;
}
- $x;
+ $self = ref($self) if ref($self);
+
+ return $upgrade->new($n) if defined $upgrade;
+
+ # hard-wired to "3"
+ $self->new(3);
+ }
+
+sub bcos
+ {
+ # Calculate cosinus(x) to N digits. Unless upgrading is in effect, returns the
+ # result truncated to an integer.
+ my ($self,$x,@r) = ref($_[0]) ? (undef,@_) : objectify(1,@_);
+
+ return $x if $x->modify('bcos');
+
+ return $x->bnan() if $x->{sign} !~ /^[+-]\z/; # -inf +inf or NaN => NaN
+
+ return $upgrade->new($x)->bcos(@r) if defined $upgrade;
+
+ # calculate the result and truncate it to integer
+ my $t = Math::BigFloat->new($x)->bcos(@r)->as_int();
+
+ $x->bone() if $t->is_one();
+ $x->bzero() if $t->is_zero();
+ $x->round(@r);
+ }
+
+sub bsin
+ {
+ # Calculate sinus(x) to N digits. Unless upgrading is in effect, returns the
+ # result truncated to an integer.
+ my ($self,$x,@r) = ref($_[0]) ? (undef,@_) : objectify(1,@_);
+
+ return $x if $x->modify('bsin');
+
+ return $x->bnan() if $x->{sign} !~ /^[+-]\z/; # -inf +inf or NaN => NaN
+
+ return $upgrade->new($x)->bsin(@r) if defined $upgrade;
+
+ # calculate the result and truncate it to integer
+ my $t = Math::BigFloat->new($x)->bsin(@r)->as_int();
+
+ $x->bone() if $t->is_one();
+ $x->bzero() if $t->is_zero();
+ $x->round(@r);
+ }
+
+sub batan
+ {
+ # Calculate arcus tangens of x to N digits. Unless upgrading is in effect, returns the
+ # result truncated to an integer.
+ my ($self,$x,@r) = ref($_[0]) ? (undef,@_) : objectify(1,@_);
+
+ return $x if $x->modify('batan');
+
+ return $x->bnan() if $x->{sign} !~ /^[+-]\z/; # -inf +inf or NaN => NaN
+
+ return $upgrade->new($x)->batan(@r) if defined $upgrade;
+
+ # calculate the result and truncate it to integer
+ my $t = Math::BigFloat->new($x)->batan(@r);
+
+ $x->{value} = $CALC->_new( $x->as_int()->bstr() );
+ $x->round(@r);
}
###############################################################################
-# this method return 0 if the object can be modified, or 1 for not
+# this method returns 0 if the object can be modified, or 1 if not.
# We use a fast constant sub() here, to avoid costly calls. Subclasses
# may override it with special code (f.i. Math::BigInt::Constant does so)
1;
__END__
+=pod
+
=head1 NAME
-Math::BigInt - Arbitrary size integer math package
+Math::BigInt - Arbitrary size integer/float math package
=head1 SYNOPSIS
# and always use (it will fall back to pure Perl if the
# GMP library is not installed):
+ # will warn if Math::BigInt::GMP cannot be found
use Math::BigInt lib => 'GMP';
+ # to supress the warning use this:
+ # use Math::BigInt try => 'GMP';
+
+ my $str = '1234567890';
+ my @values = (64,74,18);
+ my $n = 1; my $sign = '-';
+
# Number creation
- $x = Math::BigInt->new($str); # defaults to 0
- $nan = Math::BigInt->bnan(); # create a NotANumber
- $zero = Math::BigInt->bzero(); # create a +0
- $inf = Math::BigInt->binf(); # create a +inf
- $inf = Math::BigInt->binf('-'); # create a -inf
- $one = Math::BigInt->bone(); # create a +1
- $one = Math::BigInt->bone('-'); # create a -1
+ my $x = Math::BigInt->new($str); # defaults to 0
+ my $y = $x->copy(); # make a true copy
+ my $nan = Math::BigInt->bnan(); # create a NotANumber
+ my $zero = Math::BigInt->bzero(); # create a +0
+ my $inf = Math::BigInt->binf(); # create a +inf
+ my $inf = Math::BigInt->binf('-'); # create a -inf
+ my $one = Math::BigInt->bone(); # create a +1
+ my $mone = Math::BigInt->bone('-'); # create a -1
+
+ my $pi = Math::BigInt->bpi(); # returns '3'
+ # see Math::BigFloat::bpi()
+
+ $h = Math::BigInt->new('0x123'); # from hexadecimal
+ $b = Math::BigInt->new('0b101'); # from binary
+ $o = Math::BigInt->from_oct('0101'); # from octal
# Testing (don't modify their arguments)
# (return true if the condition is met, otherwise false)
$x->is_even(); # if $x is even
$x->is_pos(); # if $x >= 0
$x->is_neg(); # if $x < 0
- $x->is_inf(sign); # if $x is +inf, or -inf (sign is default '+')
+ $x->is_inf($sign); # if $x is +inf, or -inf (sign is default '+')
$x->is_int(); # if $x is an integer (not a float)
- # comparing and digit/sign extration
+ # comparing and digit/sign extraction
$x->bcmp($y); # compare numbers (undef,<0,=0,>0)
$x->bacmp($y); # compare absolutely (undef,<0,=0,>0)
$x->sign(); # return the sign, either +,- or NaN
# The following all modify their first argument. If you want to preserve
# $x, use $z = $x->copy()->bXXX($y); See under L<CAVEATS> for why this is
- # neccessary when mixing $a = $b assigments with non-overloaded math.
+ # necessary when mixing $a = $b assignments with non-overloaded math.
$x->bzero(); # set $x to 0
$x->bnan(); # set $x to NaN
$x->bdiv($y); # divide, set $x to quotient
# return (quo,rem) or quo if scalar
+ $x->bmuladd($y,$z); # $x = $x * $y + $z
+
$x->bmod($y); # modulus (x % y)
$x->bmodpow($exp,$mod); # modular exponentation (($num**$exp) % $mod))
$x->bmodinv($mod); # the inverse of $x in the given modulus $mod
$x->bpow($y); # power of arguments (x ** y)
- $x->blsft($y); # left shift
- $x->brsft($y); # right shift
- $x->blsft($y,$n); # left shift, by base $n (like 10)
- $x->brsft($y,$n); # right shift, by base $n (like 10)
+ $x->blsft($y); # left shift in base 2
+ $x->brsft($y); # right shift in base 2
+ # returns (quo,rem) or quo if in scalar context
+ $x->blsft($y,$n); # left shift by $y places in base $n
+ $x->brsft($y,$n); # right shift by $y places in base $n
+ # returns (quo,rem) or quo if in scalar context
$x->band($y); # bitwise and
$x->bior($y); # bitwise inclusive or
$x->broot($y); # $y'th root of $x (e.g. $y == 3 => cubic root)
$x->bfac(); # factorial of $x (1*2*3*4*..$x)
+ $x->bnok($y); # x over y (binomial coefficient n over k)
+
+ $x->blog(); # logarithm of $x to base e (Euler's number)
+ $x->blog($base); # logarithm of $x to base $base (f.i. 2)
+ $x->bexp(); # calculate e ** $x where e is Euler's number
+
$x->round($A,$P,$mode); # round to accuracy or precision using mode $mode
- $x->bround($N); # accuracy: preserve $N digits
- $x->bfround($N); # round to $Nth digit, no-op for BigInts
+ $x->bround($n); # accuracy: preserve $n digits
+ $x->bfround($n); # round to $nth digit, no-op for BigInts
# The following do not modify their arguments in BigInt (are no-ops),
# but do so in BigFloat:
# The following do not modify their arguments:
- bgcd(@values); # greatest common divisor (no OO style)
- blcm(@values); # lowest common multiplicator (no OO style)
+ # greatest common divisor (no OO style)
+ my $gcd = Math::BigInt::bgcd(@values);
+ # lowest common multiplicator (no OO style)
+ my $lcm = Math::BigInt::blcm(@values);
$x->length(); # return number of digits in number
- ($x,$f) = $x->length(); # length of number and length of fraction part,
- # latter is always 0 digits long for BigInt's
+ ($xl,$f) = $x->length(); # length of number and length of fraction part,
+ # latter is always 0 digits long for BigInts
$x->exponent(); # return exponent as BigInt
$x->mantissa(); # return (signed) mantissa as BigInt
$x->numify(); # return as scalar (might overflow!)
# conversation to string (do not modify their argument)
- $x->bstr(); # normalized string
- $x->bsstr(); # normalized string in scientific notation
+ $x->bstr(); # normalized string (e.g. '3')
+ $x->bsstr(); # norm. string in scientific notation (e.g. '3E0')
$x->as_hex(); # as signed hexadecimal string with prefixed 0x
$x->as_bin(); # as signed binary string with prefixed 0b
+ $x->as_oct(); # as signed octal string with prefixed 0
# precision and accuracy (see section about rounding for more)
$x->accuracy($n); # set A $x to $n
# Global methods
- Math::BigInt->precision(); # get/set global P for all BigInt objects
- Math::BigInt->accuracy(); # get/set global A for all BigInt objects
- Math::BigInt->config(); # return hash containing configuration
+ Math::BigInt->precision(); # get/set global P for all BigInt objects
+ Math::BigInt->accuracy(); # get/set global A for all BigInt objects
+ Math::BigInt->round_mode(); # get/set global round mode, one of
+ # 'even', 'odd', '+inf', '-inf', 'zero', 'trunc' or 'common'
+ Math::BigInt->config(); # return hash containing configuration
=head1 DESCRIPTION
-All operators (inlcuding basic math operations) are overloaded if you
+All operators (including basic math operations) are overloaded if you
declare your big integers as
$i = new Math::BigInt '123_456_789_123_456_789';
This means integer values like 1.01E2 or even 1000E-2 are also accepted.
Non-integer values result in NaN.
+Hexadecimal (prefixed with "0x") and binary numbers (prefixed with "0b")
+are accepted, too. Please note that octal numbers are not recognized
+by new(), so the following will print "123":
+
+ perl -MMath::BigInt -le 'print Math::BigInt->new("0123")'
+
+To convert an octal number, use from_oct();
+
+ perl -MMath::BigInt -le 'print Math::BigInt->from_oct("0123")'
+
Currently, Math::BigInt::new() defaults to 0, while Math::BigInt::new('')
results in 'NaN'. This might change in the future, so use always the following
explicit forms to get a zero or NaN:
=item Output
-Output values are BigInt objects (normalized), except for bstr(), which
-returns a string in normalized form.
+Output values are BigInt objects (normalized), except for the methods which
+return a string (see L<SYNOPSIS>).
+
Some routines (C<is_odd()>, C<is_even()>, C<is_zero()>, C<is_one()>,
-C<is_nan()>) return true or false, while others (C<bcmp()>, C<bacmp()>)
-return either undef, <0, 0 or >0 and are suited for sort.
+C<is_nan()>, etc.) return true or false, while others (C<bcmp()>, C<bacmp()>)
+return either undef (if NaN is involved), <0, 0 or >0 and are suited for sort.
=back
=head1 METHODS
Each of the methods below (except config(), accuracy() and precision())
-accepts three additional parameters. These arguments $A, $P and $R are
-accuracy, precision and round_mode. Please see the section about
+accepts three additional parameters. These arguments C<$A>, C<$P> and C<$R>
+are C<accuracy>, C<precision> and C<round_mode>. Please see the section about
L<ACCURACY and PRECISION> for more information.
-=head2 config
+=head2 config()
use Data::Dumper;
even
version version number of the class you used
1.61
- div_scale Fallback acccuracy for div
+ div_scale Fallback accuracy for div
40
trap_nan If true, traps creation of NaN via croak()
1
$new_cfg = Math::BigInt->config( { trap_inf => 1, precision => 5 } );
-=head2 accuracy
+=head2 accuracy()
$x->accuracy(5); # local for $x
CLASS->accuracy(5); # global for all members of CLASS
- $A = $x->accuracy(); # read out
- $A = CLASS->accuracy(); # read out
+ # Note: This also applies to new()!
+
+ $A = $x->accuracy(); # read out accuracy that affects $x
+ $A = CLASS->accuracy(); # read out global accuracy
Set or get the global or local accuracy, aka how many significant digits the
-results have.
+results have. If you set a global accuracy, then this also applies to new()!
+
+Warning! The accuracy I<sticks>, e.g. once you created a number under the
+influence of C<< CLASS->accuracy($A) >>, all results from math operations with
+that number will also be rounded.
+
+In most cases, you should probably round the results explicitly using one of
+L<round()>, L<bround()> or L<bfround()> or by passing the desired accuracy
+to the math operation as additional parameter:
+
+ my $x = Math::BigInt->new(30000);
+ my $y = Math::BigInt->new(7);
+ print scalar $x->copy()->bdiv($y, 2); # print 4300
+ print scalar $x->copy()->bdiv($y)->bround(2); # print 4300
Please see the section about L<ACCURACY AND PRECISION> for further details.
$y = Math::BigInt->new(1234567); # unrounded
print Math::BigInt->accuracy(4),"\n"; # set 4, print 4
- $x = Math::BigInt->new(123456); # will be automatically rounded
+ $x = Math::BigInt->new(123456); # $x will be automatically rounded!
print "$x $y\n"; # '123500 1234567'
print $x->accuracy(),"\n"; # will be 4
print $y->accuracy(),"\n"; # also 4, since global is 4
Math::BigInt and make the globals of the subclass aliases to the ones from
Math::BigInt.
-=head2 precision
+=head2 precision()
- $x->precision(-2); # local for $x, round right of the dot
- $x->precision(2); # ditto, but round left of the dot
- CLASS->accuracy(5); # global for all members of CLASS
- CLASS->precision(-5); # ditto
- $P = CLASS->precision(); # read out
- $P = $x->precision(); # read out
+ $x->precision(-2); # local for $x, round at the second digit right of the dot
+ $x->precision(2); # ditto, round at the second digit left of the dot
+
+ CLASS->precision(5); # Global for all members of CLASS
+ # This also applies to new()!
+ CLASS->precision(-5); # ditto
+
+ $P = CLASS->precision(); # read out global precision
+ $P = $x->precision(); # read out precision that affects $x
-Set or get the global or local precision, aka how many digits the result has
-after the dot (or where to round it when passing a positive number). In
-Math::BigInt, passing a negative number precision has no effect since no
-numbers have digits after the dot.
+Note: You probably want to use L<accuracy()> instead. With L<accuracy> you
+set the number of digits each result should have, with L<precision> you
+set the place where to round!
+
+C<precision()> sets or gets the global or local precision, aka at which digit
+before or after the dot to round all results. A set global precision also
+applies to all newly created numbers!
+
+In Math::BigInt, passing a negative number precision has no effect since no
+numbers have digits after the dot. In L<Math::BigFloat>, it will round all
+results to P digits after the dot.
Please see the section about L<ACCURACY AND PRECISION> for further details.
-Value must be greater than zero. Pass an undef value to disable it:
+Pass an undef value to disable it:
$x->precision(undef);
Math::BigInt->precision(undef);
Returns the current precision. For C<$x->precision()> it will return either the
local precision of $x, or if not defined, the global. This means the return
-value represents the accuracy that will be in effect for $x:
+value represents the prevision that will be in effect for $x:
$y = Math::BigInt->new(1234567); # unrounded
print Math::BigInt->precision(4),"\n"; # set 4, print 4
$x = Math::BigInt->new(123456); # will be automatically rounded
+ print $x; # print "120000"!
-Note: Works also for subclasses like Math::BigFloat. Each class has it's own
-globals separated from Math::BigInt, but it is possible to subclass
+Note: Works also for subclasses like L<Math::BigFloat>. Each class has its
+own globals separated from Math::BigInt, but it is possible to subclass
Math::BigInt and make the globals of the subclass aliases to the ones from
Math::BigInt.
-=head2 brsft
+=head2 brsft()
$x->brsft($y,$n);
This will print -3, not -2 (as it would if you divide -5 by 2 and truncate the
result).
-=head2 new
+=head2 new()
$x = Math::BigInt->new($str,$A,$P,$R);
See L<Input> for more info on accepted input formats.
-=head2 bnan
+=head2 from_oct()
+
+ $x = Math::BigIn->from_oct("0775"); # input is octal
+
+=head2 from_hex()
+
+ $x = Math::BigIn->from_hex("0xcafe"); # input is hexadecimal
+
+=head2 from_bin()
+
+ $x = Math::BigIn->from_oct("0x10011"); # input is binary
+
+=head2 bnan()
$x = Math::BigInt->bnan();
$x->bnan();
-=head2 bzero
+=head2 bzero()
$x = Math::BigInt->bzero();
$x->bzero();
-=head2 binf
+=head2 binf()
$x = Math::BigInt->binf($sign);
$x->binf();
$x->binf('-');
-=head2 bone
+=head2 bone()
$x = Math::BigInt->binf($sign);
$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
+These methods all test the BigInt for being one specific value and return
true or false depending on the input. These are faster than doing something
like:
if ($x == 0)
-=head2 is_pos()/is_neg()
+=head2 is_pos()/is_neg()/is_positive()/is_negative()
- $x->is_pos(); # true if >= 0
- $x->is_neg(); # true if < 0
+ $x->is_pos(); # true if > 0
+ $x->is_neg(); # true if < 0
The methods return true if the argument is positive or negative, respectively.
C<NaN> is neither positive nor negative, while C<+inf> counts as positive, and
-C<-inf> is negative. A C<zero> is positive.
+C<-inf> is negative. A C<zero> is neither positive nor negative.
These methods are only testing the sign, and not the value.
-C<is_positive()> and C<is_negative()> are aliase to C<is_pos()> and
+C<is_positive()> and C<is_negative()> are aliases to C<is_pos()> and
C<is_neg()>, respectively. C<is_positive()> and C<is_negative()> were
introduced in v1.36, while C<is_pos()> and C<is_neg()> were only introduced
in v1.68.
In BigInt, all numbers except C<NaN>, C<+inf> and C<-inf> are integers.
-=head2 bcmp
+=head2 bcmp()
$x->bcmp($y);
Compares $x with $y and takes the sign into account.
Returns -1, 0, 1 or undef.
-=head2 bacmp
+=head2 bacmp()
$x->bacmp($y);
Compares $x with $y while ignoring their. Returns -1, 0, 1 or undef.
-=head2 sign
+=head2 sign()
$x->sign();
Return the sign, of $x, meaning either C<+>, C<->, C<-inf>, C<+inf> or NaN.
-=head2 digit
+If you want $x to have a certain sign, use one of the following methods:
+
+ $x->babs(); # '+'
+ $x->babs()->bneg(); # '-'
+ $x->bnan(); # 'NaN'
+ $x->binf(); # '+inf'
+ $x->binf('-'); # '-inf'
+
+=head2 digit()
$x->digit($n); # return the nth digit, counting from right
If C<$n> is negative, returns the digit counting from left.
-=head2 bneg
+=head2 bneg()
$x->bneg();
Negate the number, e.g. change the sign between '+' and '-', or between '+inf'
and '-inf', respectively. Does nothing for NaN or zero.
-=head2 babs
+=head2 babs()
$x->babs();
-Set the number to it's absolute value, e.g. change the sign from '-' to '+'
+Set the number to its absolute value, e.g. change the sign from '-' to '+'
and from '-inf' to '+inf', respectively. Does nothing for NaN or positive
numbers.
-=head2 bnorm
+=head2 bnorm()
$x->bnorm(); # normalize (no-op)
-=head2 bnot
+=head2 bnot()
$x->bnot();
-Two's complement (bit wise not). This is equivalent to
+Two's complement (bitwise not). This is equivalent to
$x->binc()->bneg();
but faster.
-=head2 binc
+=head2 binc()
$x->binc(); # increment x by 1
-=head2 bdec
+=head2 bdec()
$x->bdec(); # decrement x by 1
-=head2 badd
+=head2 badd()
$x->badd($y); # addition (add $y to $x)
-=head2 bsub
+=head2 bsub()
$x->bsub($y); # subtraction (subtract $y from $x)
-=head2 bmul
+=head2 bmul()
$x->bmul($y); # multiplication (multiply $x by $y)
-=head2 bdiv
+=head2 bmuladd()
+
+ $x->bmuladd($y,$z);
+
+Multiply $x by $y, and then add $z to the result,
+
+This method was added in v1.87 of Math::BigInt (June 2007).
+
+=head2 bdiv()
$x->bdiv($y); # divide, set $x to quotient
# return (quo,rem) or quo if scalar
-=head2 bmod
+=head2 bmod()
$x->bmod($y); # modulus (x % y)
-=head2 bmodinv
+=head2 bmodinv()
num->bmodinv($mod); # modular inverse
returned unless C<$num> is relatively prime to C<$mod>, i.e. unless
C<bgcd($num, $mod)==1>.
-=head2 bmodpow
+=head2 bmodpow()
$num->bmodpow($exp,$mod); # modular exponentation
# ($num**$exp % $mod)
bmodinv($num, $mod)
-=head2 bpow
+=head2 bpow()
$x->bpow($y); # power of arguments (x ** y)
-=head2 blsft
+=head2 blog()
+
+ $x->blog($base, $accuracy); # logarithm of x to the base $base
+
+If C<$base> is not defined, Euler's number (e) is used:
+
+ print $x->blog(undef, 100); # log(x) to 100 digits
+
+=head2 bexp()
+
+ $x->bexp($accuracy); # calculate e ** X
+
+Calculates the expression C<e ** $x> where C<e> is Euler's number.
+
+This method was added in v1.82 of Math::BigInt (April 2007).
+
+See also L<blog()>.
+
+=head2 bnok()
+
+ $x->bnok($y); # x over y (binomial coefficient n over k)
+
+Calculates the binomial coefficient n over k, also called the "choose"
+function. The result is equivalent to:
+
+ ( n ) n!
+ | - | = -------
+ ( k ) k!(n-k)!
+
+This method was added in v1.84 of Math::BigInt (April 2007).
+
+=head2 bpi()
+
+ print Math::BigInt->bpi(100), "\n"; # 3
- $x->blsft($y); # left shift
+Returns PI truncated to an integer, with the argument being ignored. that
+is it always returns C<3>.
+
+If upgrading is in effect, returns PI to N digits (including the "3"
+before the dot):
+
+ use Math::BigFloat;
+ use Math::BigInt upgrade => Math::BigFloat;
+ print Math::BigInt->bpi(3), "\n"; # 3.14
+ print Math::BigInt->bpi(100), "\n"; # 3.1415....
+
+This method was added in v1.87 of Math::BigInt (June 2007).
+
+=head2 bcos()
+
+ my $x = Math::BigFloat->new(1);
+ print $x->bcos(100), "\n";
+
+Calculate the cosinus of $x, modifying $x in place.
+
+This method was added in v1.87 of Math::BigInt (June 2007).
+
+=head2 bsin()
+
+ my $x = Math::BigFloat->new(1);
+ print $x->bsin(100), "\n";
+
+Calculate the sinus of $x, modifying $x in place.
+
+This method was added in v1.87 of Math::BigInt (June 2007).
+
+=head2 batan()
+
+ my $x = Math::BigFloat->new(0.5);
+ print $x->batan(100), "\n";
+
+Calculate the arcus tanges of $x, modifying $x in place.
+
+This method was added in v1.87 of Math::BigInt (June 2007).
+
+=head2 blsft()
+
+ $x->blsft($y); # left shift in base 2
$x->blsft($y,$n); # left shift, in base $n (like 10)
-=head2 brsft
+=head2 brsft()
- $x->brsft($y); # right shift
+ $x->brsft($y); # right shift in base 2
$x->brsft($y,$n); # right shift, in base $n (like 10)
-=head2 band
+=head2 band()
$x->band($y); # bitwise and
-=head2 bior
+=head2 bior()
$x->bior($y); # bitwise inclusive or
-=head2 bxor
+=head2 bxor()
$x->bxor($y); # bitwise exclusive or
-=head2 bnot
+=head2 bnot()
$x->bnot(); # bitwise not (two's complement)
-=head2 bsqrt
+=head2 bsqrt()
$x->bsqrt(); # calculate square-root
-=head2 bfac
+=head2 bfac()
$x->bfac(); # factorial of $x (1*2*3*4*..$x)
-=head2 round
+=head2 round()
$x->round($A,$P,$round_mode);
Round $x to accuracy C<$A> or precision C<$P> using the round mode
C<$round_mode>.
-=head2 bround
+=head2 bround()
$x->bround($N); # accuracy: preserve $N digits
-=head2 bfround
+=head2 bfround()
$x->bfround($N); # round to $Nth digit, no-op for BigInts
-=head2 bfloor
+=head2 bfloor()
$x->bfloor();
Set $x to the integer less or equal than $x. This is a no-op in BigInt, but
does change $x in BigFloat.
-=head2 bceil
+=head2 bceil()
$x->bceil();
Set $x to the integer greater or equal than $x. This is a no-op in BigInt, but
does change $x in BigFloat.
-=head2 bgcd
+=head2 bgcd()
bgcd(@values); # greatest common divisor (no OO style)
-=head2 blcm
+=head2 blcm()
blcm(@values); # lowest common multiplicator (no OO style)
-head2 length
+head2 length()
$x->length();
($xl,$fl) = $x->length();
In list context, returns the length of the integer and fraction part. For
BigInt's, the length of the fraction part will always be 0.
-=head2 exponent
+=head2 exponent()
$x->exponent();
Return the exponent of $x as BigInt.
-=head2 mantissa
+=head2 mantissa()
$x->mantissa();
Return the signed mantissa of $x as BigInt.
-=head2 parts
+=head2 parts()
$x->parts(); # return (mantissa,exponent) as BigInt
-=head2 copy
+=head2 copy()
$x->copy(); # make a true copy of $x (unlike $y = $x;)
-=head2 as_int
+=head2 as_int()/as_number()
$x->as_int();
C<as_number()> is an alias to this method. C<as_number> was introduced in
v1.22, while C<as_int()> was only introduced in v1.68.
-=head2 bstr
+=head2 bstr()
$x->bstr();
-Returns a normalized string represantation of C<$x>.
+Returns a normalized string representation of C<$x>.
-=head2 bsstr
+=head2 bsstr()
$x->bsstr(); # normalized string in scientific notation
-=head2 as_hex
+=head2 as_hex()
$x->as_hex(); # as signed hexadecimal string with prefixed 0x
-=head2 as_bin
+=head2 as_bin()
$x->as_bin(); # as signed binary string with prefixed 0b
+=head2 as_oct()
+
+ $x->as_oct(); # as signed octal string with prefixed 0
+
+=head2 numify()
+
+ print $x->numify();
+
+This returns a normal Perl scalar from $x. It is used automatically
+whenever a scalar is needed, for instance in array index operations.
+
+This loses precision, to avoid this use L<as_int()> instead.
+
+=head2 modify()
+
+ $x->modify('bpowd');
+
+This method returns 0 if the object can be modified with the given
+peration, or 1 if not.
+
+This is used for instance by L<Math::BigInt::Constant>.
+
+=head2 upgrade()/downgrade()
+
+Set/get the class for downgrade/upgrade operations. Thuis is used
+for instance by L<bignum>. The defaults are '', thus the following
+operation will create a BigInt, not a BigFloat:
+
+ my $i = Math::BigInt->new(123);
+ my $f = Math::BigFloat->new('123.1');
+
+ print $i + $f,"\n"; # print 246
+
+=head2 div_scale()
+
+Set/get the number of digits for the default precision in divide
+operations.
+
+=head2 round_mode()
+
+Set/get the current round mode.
+
=head1 ACCURACY and PRECISION
Since version v1.33, Math::BigInt and Math::BigFloat have full support for
E.g., when rounding to the first sigdig, 0.45 becomes 0.4, -0.55
becomes -0.5, but 0.4501 becomes 0.5.
+=item 'common'
+
+round up if the digit immediately to the right of the rounding place
+is 5 or greater, otherwise round down. E.g., 0.15 becomes 0.2 and
+0.149 becomes 0.1.
+
=back
The handling of A & P in MBI/MBF (the old core code shipped with Perl
result has at most max(scale, length(dividend), length(divisor)) digits
Actual code:
scale = max(scale, length(dividend)-1,length(divisor)-1);
- scale += length(divisior) - length(dividend);
+ scale += length(divisor) - length(dividend);
So for lx = 3, ly = 9, scale = 10, scale will actually be 16 (10+9-3).
Actually, the 'difference' added to the scale is calculated from the
number of "significant digits" in dividend and divisor, which is derived
* to find out the current global P, use C<< Math::SomeClass->precision() >>
* use C<< $x->accuracy() >> respective C<< $x->precision() >> for the local
setting of C<< $x >>.
- * Please note that C<< $x->accuracy() >> respecive C<< $x->precision() >>
+ * Please note that C<< $x->accuracy() >> respective C<< $x->precision() >>
return eventually defined global A or P, when C<< $x >>'s A or P is not
set.
=item Creating numbers
- * When you create a number, you can give it's desired A or P via:
+ * When you create a number, you can give the desired A or P via:
$x = Math::BigInt->new($number,$A,$P);
* Only one of A or P can be defined, otherwise the result is NaN
* If no A or P is give ($x = Math::BigInt->new($number) form), then the
$x will be what was in effect when $x was created)
* If given undef for A and P, B<no> rounding will occur, and the globals will
B<not> be used. This is used by subclasses to create numbers without
- suffering rounding in the parent. Thus a subclass is able to have it's own
+ suffering rounding in the parent. Thus a subclass is able to have its own
globals enforced upon creation of a number by using
C<< $x = Math::BigInt->new($number,undef,undef) >>:
be automatically cleared.
* If two objects are involved in an operation, and one of them has A in
effect, and the other P, this results in an error (NaN).
- * A takes precendence over P (Hint: A comes before P).
+ * A takes precedence over P (Hint: A comes before P).
If neither of them is defined, nothing is used, i.e. the result will have
as many digits as it can (with an exception for fdiv/fsqrt) and will not
be rounded.
+ never round (this is the default):
This is done by setting A and P to undef. No math operation
will round the result, with fdiv() and fsqrt() as exceptions to guard
- against overflows. You must explicitely call bround(), bfround() or
+ against overflows. You must explicitly call bround(), bfround() or
round() (the latter with parameters).
Note: Once you have rounded a number, the settings will 'stick' on it
and 'infect' all other numbers engaged in math operations with it, since
is for precision
* the two rounding functions take as the second parameter one of the
following rounding modes (R):
- 'even', 'odd', '+inf', '-inf', 'zero', 'trunc'
+ 'even', 'odd', '+inf', '-inf', 'zero', 'trunc', 'common'
* you can set/get the global R by using C<< Math::SomeClass->round_mode() >>
or by setting C<< $Math::SomeClass::round_mode >>
* after each operation, C<< $result->round() >> is called, and the result may
=back
+=head1 Infinity and Not a Number
+
+While BigInt has extensive handling of inf and NaN, certain quirks remain.
+
+=over 2
+
+=item oct()/hex()
+
+These perl routines currently (as of Perl v.5.8.6) cannot handle passed
+inf.
+
+ te@linux:~> perl -wle 'print 2 ** 3333'
+ inf
+ te@linux:~> perl -wle 'print 2 ** 3333 == 2 ** 3333'
+ 1
+ te@linux:~> perl -wle 'print oct(2 ** 3333)'
+ 0
+ te@linux:~> perl -wle 'print hex(2 ** 3333)'
+ Illegal hexadecimal digit 'i' ignored at -e line 1.
+ 0
+
+The same problems occur if you pass them Math::BigInt->binf() objects. Since
+overloading these routines is not possible, this cannot be fixed from BigInt.
+
+=item ==, !=, <, >, <=, >= with NaNs
+
+BigInt's bcmp() routine currently returns undef to signal that a NaN was
+involved in a comparison. However, the overload code turns that into
+either 1 or '' and thus operations like C<< NaN != NaN >> might return
+wrong values.
+
+=item log(-inf)
+
+C<< log(-inf) >> is highly weird. Since log(-x)=pi*i+log(x), then
+log(-inf)=pi*i+inf. However, since the imaginary part is finite, the real
+infinity "overshadows" it, so the number might as well just be infinity.
+However, the result is a complex number, and since BigInt/BigFloat can only
+have real numbers as results, the result is NaN.
+
+=item exp(), cos(), sin(), atan2()
+
+These all might have problems handling infinity right.
+
+=back
+
=head1 INTERNALS
The actual numbers are stored as unsigned big integers (with seperate sign).
+
You should neither care about nor depend on the internal representation; it
-might change without notice. Use only method calls like C<< $x->sign(); >>
-instead relying on the internal hash keys like in C<< $x->{sign}; >>.
+might change without notice. Use B<ONLY> method calls like C<< $x->sign(); >>
+instead relying on the internal representation.
=head2 MATH LIBRARY
use Math::BigInt lib => 'Foo,Math::BigInt::Bar';
Since Math::BigInt::GMP is in almost all cases faster than Calc (especially in
-cases involving really big numbers, where it is B<much> faster), and there is
+math involving really big numbers, where it is B<much> faster), and there is
no penalty if Math::BigInt::GMP is not installed, it is a good idea to always
use the following:
use Math::BigInt lib => 'GMP';
Different low-level libraries use different formats to store the
-numbers. You should not depend on the number having a specific format.
+numbers. You should B<NOT> depend on the number having a specific format
+internally.
See the respective math library module documentation for further details.
=head2 SIGN
-The sign is either '+', '-', 'NaN', '+inf' or '-inf' and stored seperately.
+The sign is either '+', '-', 'NaN', '+inf' or '-inf'.
A sign of 'NaN' is used to represent the result when input arguments are not
numbers or as a result of 0/0. '+inf' and '-inf' represent plus respectively
C<< ($m,$e) = $x->parts() >> is just a shortcut that gives you both of them
in one go. Both the returned mantissa and exponent have a sign.
-Currently, for BigInts C<$e> is always 0, except for NaN, +inf and -inf,
-where it is C<NaN>; and for C<$x == 0>, where it is C<1> (to be compatible
-with Math::BigFloat's internal representation of a zero as C<0E1>).
+Currently, for BigInts C<$e> is always 0, except +inf and -inf, where it is
+C<+inf>; and for NaN, where it is C<NaN>; and for C<$x == 0>, where it is C<1>
+(to be compatible with Math::BigFloat's internal representation of a zero as
+C<0E1>).
C<$m> is currently just a copy of the original number. The relation between
C<$e> and C<$m> will stay always the same, though their real values might
$x = "$x"; # same as bstr()
$x = Math::BigInt->bneg("1234"); # BigInt "-1234"
$x = Math::BigInt->babs("-12345"); # BigInt "12345"
- $x = Math::BigInt->bnorm("-0 00"); # BigInt "0"
+ $x = Math::BigInt->bnorm("-0.00"); # BigInt "0"
$x = bint(1) + bint(2); # BigInt "3"
$x = bint(1) + "2"; # ditto (auto-BigIntify of "2")
$x = bint(1); # BigInt "1"
With a technique called copy-on-write, the cost of copying with overload could
be minimized or even completely avoided. A test implementation of COW did show
performance gains for overloaded math, but introduced a performance loss due
-to a constant overhead for all other operatons. So Math::BigInt does currently
+to a constant overhead for all other operations. So Math::BigInt does currently
not COW.
The rewritten version of this module (vs. v0.01) is slower on certain
All other object methods and overloaded functions can be directly inherited
from the parent class.
-At the very minimum, any subclass will need to provide it's own C<new()> and can
+At the very minimum, any subclass will need to provide its own C<new()> and can
store additional hash keys in the object. There are also some package globals
that must be defined, e.g.:
=item blog()
+=item bexp()
+
=back
Beware: This list is not complete.
arguments are of the class mentioned in $upgrade (This might change in later
versions to a more sophisticated scheme):
+=head1 EXPORTS
+
+C<Math::BigInt> exports nothing by default, but can export the following methods:
+
+ bgcd
+ blcm
+
=head1 BUGS
=over 2
drop the leading '+'. The old code would return '+3', the new returns '3'.
This is to be consistent with Perl and to make C<cmp> (especially with
overloading) to work as you expect. It also solves problems with C<Test.pm>,
-because it's C<ok()> uses 'eq' internally.
+because its C<ok()> uses 'eq' internally.
Mark Biggar said, when asked about to drop the '+' altogether, or make only
C<cmp> work:
There is now a C<bsstr()> method to get the string in scientific notation aka
C<1e+2> instead of C<100>. Be advised that overloaded 'eq' always uses bstr()
-for comparisation, but Perl will represent some numbers as 100 and others
+for comparison, but Perl will represent some numbers as 100 and others
as 1e+308. If in doubt, convert both arguments to Math::BigInt before
comparing them as strings:
$y = Math::BigInt->new($y);
ok ($x,$y); # okay
-Alternatively, simple use C<< <=> >> for comparisations, this will get it
+Alternatively, simple use C<< <=> >> for comparisons, this will get it
always right. There is not yet a way to get a number automatically represented
as a string that matches exactly the way Perl represents it.
+See also the section about L<Infinity and Not a Number> for problems in
+comparing NaNs.
+
=item int()
C<int()> will return (at least for Perl v5.7.1 and up) another BigInt, not a
$x = Math::BigFloat->new(123.45);
$y = int($x); # BigInt 123
-In all Perl versions you can use C<as_number()> for the same effect:
+In all Perl versions you can use C<as_number()> or C<as_int> for the same
+effect:
$x = Math::BigFloat->new(123.45);
$y = $x->as_number(); # BigInt 123
+ $y = $x->as_int(); # ditto
This also works for other subclasses, like Math::String.
-It is yet unlcear whether overloaded int() should return a scalar or a BigInt.
+If you want a real Perl scalar, use C<numify()>:
+
+ $y = $x->numify(); # 123 as scalar
+
+This is seldom necessary, though, because this is done automatically, like
+when you access an array:
+
+ $z = $array[$x]; # does work automatically
=item length
print $c->bdiv(10000),"\n";
It prints both quotient and remainder since print calls C<bdiv()> in list
-context. Also, C<bdiv()> will modify $c, so be carefull. You probably want
+context. Also, C<bdiv()> will modify $c, so be careful. You probably want
to use
print $c / 10000,"\n";
holds true for any $x and $y, which justifies calling the two return
values of bdiv() the quotient and remainder. The only exception to this rule
are when $y == 0 and $x is negative, then the remainder will also be
-negative. See below under "infinity handling" for the reasoning behing this.
+negative. See below under "infinity handling" for the reasoning behind this.
Perl's 'use integer;' changes the behaviour of % and / for scalars, but will
not change BigInt's way to do things. This is because under 'use integer' Perl
needs to preserve $x since it does not know that it later will get overwritten.
This makes a copy of $x and takes O(N), but $x->bneg() is O(1).
-With Copy-On-Write, this issue would be gone, but C-o-W is not implemented
-since it is slower for all other things.
-
=item Mixing different object types
In Perl you will get a floating point value if you do one of the following:
=head1 AUTHORS
Original code by Mark Biggar, overloaded interface by Ilya Zakharevich.
-Completely rewritten by Tels http://bloodgate.com in late 2000, 2001, 2002
-and still at it in 2003.
+Completely rewritten by Tels http://bloodgate.com in late 2000, 2001 - 2006
+and still at it in 2007.
Many people contributed in one or more ways to the final beast, see the file
-CREDITS for an (uncomplete) list. If you miss your name, please drop me a
+CREDITS for an (incomplete) list. If you miss your name, please drop me a
mail. Thank you!
=cut