-#!/usr/bin/perl -w
+package Math::BigFloat;
+
+#
+# Mike grinned. 'Two down, infinity to go' - Mike Nostrus in 'Before and After'
+#
# The following hash values are internally used:
# _e: exponent (BigInt)
# _a: accuracy
# _p: precision
# _f: flags, used to signal MBI not to touch our private parts
-# _cow: Copy-On-Write (NRY)
-
-package Math::BigFloat;
-$VERSION = '1.21';
+$VERSION = '1.32';
require 5.005;
use Exporter;
-use Math::BigInt qw/objectify/;
+use File::Spec;
+# use Math::BigInt;
@ISA = qw( Exporter Math::BigInt);
-# can not export bneg/babs since the are only in MBI
-@EXPORT_OK = qw(
- bcmp
- badd bmul bdiv bmod bnorm bsub
- bgcd blcm bround bfround
- bpow bnan bzero bfloor bceil
- bacmp bstr binc bdec binf
- is_odd is_even is_nan is_inf is_positive is_negative
- is_zero is_one sign
- );
-
-#@EXPORT = qw( );
+
use strict;
-use vars qw/$AUTOLOAD $accuracy $precision $div_scale $rnd_mode/;
+use vars qw/$AUTOLOAD $accuracy $precision $div_scale $round_mode $rnd_mode/;
+use vars qw/$upgrade $downgrade/;
my $class = "Math::BigFloat";
use overload
# constant for easier life
my $nan = 'NaN';
-# Rounding modes one of 'even', 'odd', '+inf', '-inf', 'zero' or 'trunc'
-$rnd_mode = 'even';
-$accuracy = undef;
-$precision = undef;
-$div_scale = 40;
+# class constants, use Class->constant_name() to access
+$round_mode = 'even'; # one of 'even', 'odd', '+inf', '-inf', 'zero' or 'trunc'
+$accuracy = undef;
+$precision = undef;
+$div_scale = 40;
+
+$upgrade = undef;
+$downgrade = undef;
+my $MBI = 'Math::BigInt'; # the package we are using for our private parts
+ # changable by use Math::BigFloat with => 'package'
+
+##############################################################################
+# the old code had $rnd_mode, so we need to support it, too
+
+sub TIESCALAR { my ($class) = @_; bless \$round_mode, $class; }
+sub FETCH { return $round_mode; }
+sub STORE { $rnd_mode = $_[0]->round_mode($_[1]); }
+
+BEGIN
+ {
+ $rnd_mode = 'even';
+ tie $rnd_mode, 'Math::BigFloat';
+ }
+
+##############################################################################
# in case we call SUPER::->foo() and this wants to call modify()
# sub modify () { 0; }
{
- # checks for AUTOLOAD
+ # valid method aliases for AUTOLOAD
my %methods = map { $_ => 1 }
qw / fadd fsub fmul fdiv fround ffround fsqrt fmod fstr fsstr fpow fnorm
- fabs fneg fint fcmp fzero fnan finc fdec
+ fint facmp fcmp fzero fnan finf finc fdec flog ffac
+ fceil ffloor frsft flsft fone flog
+ /;
+ # valid method's that can be hand-ed up (for AUTOLOAD)
+ my %hand_ups = map { $_ => 1 }
+ qw / is_nan is_inf is_negative is_positive
+ accuracy precision div_scale round_mode fneg fabs babs fnot
+ objectify upgrade downgrade
+ bone binf bnan bzero
/;
- sub method_valid { return exists $methods{$_[0]||''}; }
+ sub method_alias { return exists $methods{$_[0]||''}; }
+ sub method_hand_up { return exists $hand_ups{$_[0]||''}; }
}
##############################################################################
# _m: mantissa
# sign => sign (+/-), or "NaN"
- my $class = shift;
-
- my $wanted = shift; # avoid numify call by not using || here
- return $class->bzero() if !defined $wanted; # default to 0
- return $wanted->copy() if ref($wanted) eq $class;
+ my ($class,$wanted,@r) = @_;
+
+ # avoid numify-calls by not using || on $wanted!
+ return $class->bzero() if !defined $wanted; # default to 0
+ return $wanted->copy() if UNIVERSAL::isa($wanted,'Math::BigFloat');
- my $round = shift; $round = 0 if !defined $round; # no rounding as default
my $self = {}; bless $self, $class;
# shortcut for bigints and its subclasses
if ((ref($wanted)) && (ref($wanted) ne $class))
{
$self->{_m} = $wanted->as_number(); # get us a bigint copy
- $self->{_e} = Math::BigInt->new(0);
+ $self->{_e} = $MBI->bzero();
$self->{_m}->babs();
$self->{sign} = $wanted->sign();
return $self->bnorm();
}
# got string
# handle '+inf', '-inf' first
- if ($wanted =~ /^[+-]inf$/)
+ if ($wanted =~ /^[+-]?inf$/)
{
- $self->{_e} = Math::BigInt->new(0);
- $self->{_m} = Math::BigInt->new(0);
+ return $downgrade->new($wanted) if $downgrade;
+
+ $self->{_e} = $MBI->bzero();
+ $self->{_m} = $MBI->bzero();
$self->{sign} = $wanted;
+ $self->{sign} = '+inf' if $self->{sign} eq 'inf';
return $self->bnorm();
}
#print "new string '$wanted'\n";
if (!ref $mis)
{
die "$wanted is not a number initialized to $class" if !$NaNOK;
- $self->{_e} = Math::BigInt->new(0);
- $self->{_m} = Math::BigInt->new(0);
+
+ return $downgrade->bnan() if $downgrade;
+
+ $self->{_e} = $MBI->bzero();
+ $self->{_m} = $MBI->bzero();
$self->{sign} = $nan;
}
else
{
# make integer from mantissa by adjusting exp, then convert to bigint
- $self->{_e} = Math::BigInt->new("$$es$$ev"); # exponent
- $self->{_m} = Math::BigInt->new("$$mis$$miv$$mfv"); # create mantissa
+ # undef,undef to signal MBI that we don't need no bloody rounding
+ $self->{_e} = $MBI->new("$$es$$ev",undef,undef); # exponent
+ $self->{_m} = $MBI->new("$$miv$$mfv",undef,undef); # create mant.
# 3.123E0 = 3123E-3, and 3.123E-2 => 3123E-5
- $self->{_e} -= CORE::length($$mfv);
- $self->{sign} = $self->{_m}->sign(); $self->{_m}->babs();
- }
- #print "$wanted => $self->{sign} $self->{value}\n";
- $self->bnorm(); # first normalize
- # if any of the globals is set, round to them and thus store them insid $self
- $self->round($accuracy,$precision,$rnd_mode)
- if defined $accuracy || defined $precision;
- return $self;
+ $self->{_e} -= CORE::length($$mfv) if CORE::length($$mfv) != 0;
+ $self->{sign} = $$mis;
+ }
+ # if downgrade, inf, NaN or integers go down
+
+ if ($downgrade && $self->{_e}->{sign} eq '+')
+ {
+# print "downgrading $$miv$$mfv"."E$$es$$ev";
+ if ($self->{_e}->is_zero())
+ {
+ $self->{_m}->{sign} = $$mis; # negative if wanted
+ return $downgrade->new($self->{_m});
+ }
+ return $downgrade->new("$$mis$$miv$$mfv"."E$$es$$ev");
+ }
+ # print "mbf new $self->{sign} $self->{_m} e $self->{_e} ",ref($self),"\n";
+ $self->bnorm()->round(@r); # first normalize, then round
}
-sub bnan
+sub _bnan
{
- # create a bigfloat 'NaN', if given a BigFloat, set it to 'NaN'
+ # used by parent class bone() to initialize number to 1
my $self = shift;
- $self = $class if !defined $self;
- if (!ref($self))
- {
- my $c = $self; $self = {}; bless $self, $c;
- }
- $self->{_m} = Math::BigInt->bzero();
- $self->{_e} = Math::BigInt->bzero();
- $self->{sign} = $nan;
- return $self;
+ $self->{_m} = $MBI->bzero();
+ $self->{_e} = $MBI->bzero();
}
-sub binf
+sub _binf
{
- # create a bigfloat '+-inf', if given a BigFloat, set it to '+-inf'
+ # used by parent class bone() to initialize number to 1
my $self = shift;
- my $sign = shift; $sign = '+' if !defined $sign || $sign ne '-';
+ $self->{_m} = $MBI->bzero();
+ $self->{_e} = $MBI->bzero();
+ }
- $self = $class if !defined $self;
- if (!ref($self))
- {
- my $c = $self; $self = {}; bless $self, $c;
- }
- $self->{_m} = Math::BigInt->bzero();
- $self->{_e} = Math::BigInt->bzero();
- $self->{sign} = $sign.'inf';
- return $self;
+sub _bone
+ {
+ # used by parent class bone() to initialize number to 1
+ my $self = shift;
+ $self->{_m} = $MBI->bone();
+ $self->{_e} = $MBI->bzero();
}
-sub bone
+sub _bzero
{
- # create a bigfloat '+-1', if given a BigFloat, set it to '+-1'
+ # used by parent class bone() to initialize number to 1
my $self = shift;
- my $sign = shift; $sign = '+' if !defined $sign || $sign ne '-';
+ $self->{_m} = $MBI->bzero();
+ $self->{_e} = $MBI->bone();
+ }
- $self = $class if !defined $self;
- if (!ref($self))
- {
- my $c = $self; $self = {}; bless $self, $c;
- }
- $self->{_m} = Math::BigInt->bone();
- $self->{_e} = Math::BigInt->bzero();
- $self->{sign} = $sign;
- return $self;
+sub isa
+ {
+ my ($self,$class) = @_;
+ return if $class =~ /^Math::BigInt/; # we aren't one of these
+ UNIVERSAL::isa($self,$class);
}
-sub bzero
+sub config
{
- # create a bigfloat '+0', if given a BigFloat, set it to 0
- my $self = shift;
- $self = $class if !defined $self;
- if (!ref($self))
+ # return (later set?) configuration data as hash ref
+ my $class = shift || 'Math::BigFloat';
+
+ my $cfg = $MBI->config();
+
+ no strict 'refs';
+ $cfg->{class} = $class;
+ $cfg->{with} = $MBI;
+ foreach (
+ qw/upgrade downgrade precision accuracy round_mode VERSION div_scale/)
{
- my $c = $self; $self = {}; bless $self, $c;
- }
- $self->{_m} = Math::BigInt->bzero();
- $self->{_e} = Math::BigInt->bone();
- $self->{sign} = '+';
- return $self;
+ $cfg->{lc($_)} = ${"${class}::$_"};
+ };
+ $cfg;
}
##############################################################################
# (ref to BFLOAT or num_str ) return num_str
# Convert number from internal format to (non-scientific) string format.
# internal format is always normalized (no leading zeros, "-0" => "+0")
- my ($self,$x) = objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ #my $x = shift; my $class = ref($x) || $x;
+ #$x = $class->new(shift) unless ref($x);
#die "Oups! e was $nan" if $x->{_e}->{sign} eq $nan;
#die "Oups! m was $nan" if $x->{_m}->{sign} eq $nan;
my $es = '0'; my $len = 1; my $cad = 0; my $dot = '.';
- my $not_zero = !$x->is_zero();
+ my $not_zero = ! $x->is_zero();
if ($not_zero)
{
$es = $x->{_m}->bstr();
$len = CORE::length($es);
if (!$x->{_e}->is_zero())
-# {
-# $es = $x->{sign}.$es if $x->{sign} eq '-';
-# }
-# else
{
if ($x->{_e}->sign() eq '-')
{
# 123400 => 6, 0.1234 => 4, 0.001234 => 4
my $zeros = $x->{_a} - $cad; # cad == 0 => 12340
$zeros = $x->{_a} - $len if $cad != $len;
- #print "acc padd $x->{_a} $zeros (len $len cad $cad)\n";
$es .= $dot.'0' x $zeros if $zeros > 0;
}
elsif ($x->{_p} || 0 < 0)
{
# 123400 => 6, 0.1234 => 4, 0.001234 => 6
my $zeros = -$x->{_p} + $cad;
- #print "pre padd $x->{_p} $zeros (len $len cad $cad)\n";
$es .= $dot.'0' x $zeros if $zeros > 0;
}
- return $es;
+ $es;
}
sub bsstr
# (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 ($self,$x) = objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ #my $x = shift; my $class = ref($x) || $x;
+ #$x = $class->new(shift) unless ref($x);
#die "Oups! e was $nan" if $x->{_e}->{sign} eq $nan;
#die "Oups! m was $nan" if $x->{_m}->{sign} eq $nan;
}
my $sign = $x->{_e}->{sign}; $sign = '' if $sign eq '-';
my $sep = 'e'.$sign;
- return $x->{_m}->bstr().$sep.$x->{_e}->bstr();
+ $x->{_m}->bstr().$sep.$x->{_e}->bstr();
}
sub numify
{
# Make a number from a BigFloat object
# simple return string and let Perl's atoi()/atof() handle the rest
- my ($self,$x) = objectify(1,@_);
- return $x->bsstr();
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ $x->bsstr();
}
##############################################################################
# public stuff (usually prefixed with "b")
-# really? Just for exporting them is not what I had in mind
-#sub babs
-# {
-# $class->SUPER::babs($class,@_);
-# }
-#sub bneg
-# {
-# $class->SUPER::bneg($class,@_);
-# }
-
# tels 2001-08-04
# todo: this must be overwritten and return NaN for non-integer values
# band(), bior(), bxor(), too
return +1 if $x->{sign} eq '+inf';
return -1 if $x->{sign} eq '-inf';
return -1 if $y->{sign} eq '+inf';
- return +1 if $y->{sign} eq '-inf';
+ return +1;
}
# check sign for speed first
# adjust so that exponents are equal
my $lxm = $x->{_m}->length();
my $lym = $y->{_m}->length();
- my $lx = $lxm + $x->{_e};
- my $ly = $lym + $y->{_e};
- # print "x $x y $y lx $lx ly $ly\n";
+ # the numify somewhat limits our length, but makes it much faster
+ my $lx = $lxm + $x->{_e}->numify();
+ my $ly = $lym + $y->{_e}->numify();
my $l = $lx - $ly; $l = -$l if $x->{sign} eq '-';
- # print "$l $x->{sign}\n";
return $l <=> 0 if $l != 0;
# lengths (corrected by exponent) are equal
- # so make mantissa euqal length by padding with zero (shift left)
+ # so make mantissa equal length by padding with zero (shift left)
my $diff = $lxm - $lym;
my $xm = $x->{_m}; # not yet copy it
my $ym = $y->{_m};
{
$xm = $x->{_m}->copy()->blsft(-$diff,10);
}
- my $rc = $xm->bcmp($ym);
+ my $rc = $xm->bacmp($ym);
$rc = -$rc if $x->{sign} eq '-'; # -124 < -123
- return $rc <=> 0;
+ $rc <=> 0;
}
sub bacmp
# Returns one of undef, <0, =0, >0. (suitable for sort)
# (BFLOAT or num_str, BFLOAT or num_str) return cond_code
my ($self,$x,$y) = objectify(2,@_);
- return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
-
- # signs are ignored, so check length
- # length(x) is length(m)+e aka length of non-fraction part
- # the longer one is bigger
- my $l = $x->length() - $y->length();
- #print "$l\n";
- return $l if $l != 0;
- #print "equal lengths\n";
-
- # if both are equal long, make full compare
- # first compare only the mantissa
- # if mantissa are equal, compare fractions
+
+ # handle +-inf and NaN's
+ if ($x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/)
+ {
+ return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
+ return 0 if ($x->is_inf() && $y->is_inf());
+ return 1 if ($x->is_inf() && !$y->is_inf());
+ return -1;
+ }
+
+ # shortcut
+ my $xz = $x->is_zero();
+ my $yz = $y->is_zero();
+ return 0 if $xz && $yz; # 0 <=> 0
+ return -1 if $xz && !$yz; # 0 <=> +y
+ return 1 if $yz && !$xz; # +x <=> 0
+
+ # adjust so that exponents are equal
+ my $lxm = $x->{_m}->length();
+ my $lym = $y->{_m}->length();
+ # the numify somewhat limits our length, but makes it much faster
+ my $lx = $lxm + $x->{_e}->numify();
+ my $ly = $lym + $y->{_e}->numify();
+ my $l = $lx - $ly;
+ return $l <=> 0 if $l != 0;
- return $x->{_m} <=> $y->{_m} || $x->{_e} <=> $y->{_e};
+ # lengths (corrected by exponent) are equal
+ # so make mantissa equal-length by padding with zero (shift left)
+ my $diff = $lxm - $lym;
+ my $xm = $x->{_m}; # not yet copy it
+ my $ym = $y->{_m};
+ if ($diff > 0)
+ {
+ $ym = $y->{_m}->copy()->blsft($diff,10);
+ }
+ elsif ($diff < 0)
+ {
+ $xm = $x->{_m}->copy()->blsft(-$diff,10);
+ }
+ $xm->bacmp($ym) <=> 0;
}
sub badd
{
# NaN first
return $x->bnan() if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
- # inf handline
+ # inf handling
if (($x->{sign} =~ /^[+-]inf$/) && ($y->{sign} =~ /^[+-]inf$/))
{
- # + and + => +, - and - => -, + and - => 0, - and + => 0
- return $x->bzero() if $x->{sign} ne $y->{sign};
- return $x;
+ # +inf++inf or -inf+-inf => same, rest is NaN
+ return $x if $x->{sign} eq $y->{sign};
+ return $x->bnan();
}
- # +-inf + something => +inf
- # something +-inf => +-inf
+ # +-inf + something => +inf; something +-inf => +-inf
$x->{sign} = $y->{sign}, return $x if $y->{sign} =~ /^[+-]inf$/;
return $x;
}
+ return $upgrade->badd($x,$y,$a,$p,$r) if defined $upgrade &&
+ ((!$x->isa($self)) || (!$y->isa($self)));
+
# speed: no add for 0+y or x+0
- return $x if $y->is_zero(); # x+0
+ return $x->bround($a,$p,$r) if $y->is_zero(); # x+0
if ($x->is_zero()) # 0+y
{
# make copy, clobbering up x (modify in place!)
}
# take lower of the two e's and adapt m1 to it to match m2
- my $e = $y->{_e}; $e = Math::BigInt::bzero() if !defined $e; # if no BFLOAT
- $e = $e - $x->{_e};
+ my $e = $y->{_e};
+ $e = $MBI->bzero() if !defined $e; # if no BFLOAT ?
+ $e = $e->copy(); # make copy (didn't do it yet)
+ $e->bsub($x->{_e});
my $add = $y->{_m}->copy();
- if ($e < 0)
+ if ($e->{sign} eq '-') # < 0
{
- # print "e < 0\n";
- #print "\$x->{_m}: $x->{_m} ";
- #print "\$x->{_e}: $x->{_e}\n";
my $e1 = $e->copy()->babs();
- $x->{_m} *= (10 ** $e1);
+ #$x->{_m} *= (10 ** $e1);
+ $x->{_m}->blsft($e1,10);
$x->{_e} += $e; # need the sign of e
- #$x->{_m} += $y->{_m};
- #print "\$x->{_m}: $x->{_m} ";
- #print "\$x->{_e}: $x->{_e}\n";
}
- elsif ($e > 0)
+ elsif (!$e->is_zero()) # > 0
{
- # print "e > 0\n";
- #print "\$x->{_m}: $x->{_m} \$y->{_m}: $y->{_m} \$e: $e ",ref($e),"\n";
- $add *= (10 ** $e);
- #$x->{_m} += $y->{_m} * (10 ** $e);
- #print "\$x->{_m}: $x->{_m}\n";
+ #$add *= (10 ** $e);
+ $add->blsft($e,10);
}
- # else: both e are same, so leave them
- #print "badd $x->{sign}$x->{_m} + $y->{sign}$add\n";
- # fiddle with signs
- $x->{_m}->{sign} = $x->{sign};
+ # else: both e are the same, so just leave them
+ $x->{_m}->{sign} = $x->{sign}; # fiddle with signs
$add->{sign} = $y->{sign};
- # finally do add/sub
- $x->{_m} += $add;
- # re-adjust signs
- $x->{sign} = $x->{_m}->{sign};
- $x->{_m}->{sign} = '+';
- #$x->bnorm(); # delete trailing zeros
- return $x->round($a,$p,$r,$y);
+ $x->{_m} += $add; # finally do add/sub
+ $x->{sign} = $x->{_m}->{sign}; # re-adjust signs
+ $x->{_m}->{sign} = '+'; # mantissa always positiv
+ # delete trailing zeros, then round
+ return $x->bnorm()->round($a,$p,$r,$y);
}
sub bsub
{
# (BigFloat or num_str, BigFloat or num_str) return BigFloat
# subtract second arg from first, modify first
- my ($self,$x,$y) = objectify(2,@_);
+ my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
- $x->badd($y->bneg()); # badd does not leave internal zeros
- $y->bneg(); # refix y, assumes no one reads $y in between
- return $x; # badd() already normalized and rounded
+ if ($y->is_zero()) # still round for not adding zero
+ {
+ return $x->round($a,$p,$r);
+ }
+
+ $y->{sign} =~ tr/+\-/-+/; # does nothing for NaN
+ $x->badd($y,$a,$p,$r); # badd does not leave internal zeros
+ $y->{sign} =~ tr/+\-/-+/; # refix $y (does nothing for NaN)
+ $x; # already rounded by badd()
}
sub binc
{
# increment arg by one
- my ($self,$x,$a,$p,$r) = objectify(1,@_);
- $x->badd($self->_one())->round($a,$p,$r);
+ my ($self,$x,$a,$p,$r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
+
+ if ($x->{_e}->sign() eq '-')
+ {
+ return $x->badd($self->bone(),$a,$p,$r); # digits after dot
+ }
+
+ if (!$x->{_e}->is_zero())
+ {
+ $x->{_m}->blsft($x->{_e},10); # 1e2 => 100
+ $x->{_e}->bzero();
+ }
+ # now $x->{_e} == 0
+ if ($x->{sign} eq '+')
+ {
+ $x->{_m}->binc();
+ return $x->bnorm()->bround($a,$p,$r);
+ }
+ elsif ($x->{sign} eq '-')
+ {
+ $x->{_m}->bdec();
+ $x->{sign} = '+' if $x->{_m}->is_zero(); # -1 +1 => -0 => +0
+ return $x->bnorm()->bround($a,$p,$r);
+ }
+ # inf, nan handling etc
+ $x->badd($self->__one(),$a,$p,$r); # does round
}
sub bdec
{
# decrement arg by one
- my ($self,$x,$a,$p,$r) = objectify(1,@_);
- $x->badd($self->_one('-'))->round($a,$p,$r);
+ my ($self,$x,$a,$p,$r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
+
+ if ($x->{_e}->sign() eq '-')
+ {
+ return $x->badd($self->bone('-'),$a,$p,$r); # digits after dot
+ }
+
+ if (!$x->{_e}->is_zero())
+ {
+ $x->{_m}->blsft($x->{_e},10); # 1e2 => 100
+ $x->{_e}->bzero();
+ }
+ # now $x->{_e} == 0
+ my $zero = $x->is_zero();
+ # <= 0
+ if (($x->{sign} eq '-') || $zero)
+ {
+ $x->{_m}->binc();
+ $x->{sign} = '-' if $zero; # 0 => 1 => -1
+ $x->{sign} = '+' if $x->{_m}->is_zero(); # -1 +1 => -0 => +0
+ return $x->bnorm()->round($a,$p,$r);
+ }
+ # > 0
+ elsif ($x->{sign} eq '+')
+ {
+ $x->{_m}->bdec();
+ return $x->bnorm()->round($a,$p,$r);
+ }
+ # inf, nan handling etc
+ $x->badd($self->bone('-'),$a,$p,$r); # does round
}
+sub blog
+ {
+ my ($self,$x,$base,$a,$p,$r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(2,@_);
+
+ # http://www.efunda.com/math/taylor_series/logarithmic.cfm?search_string=log
+
+ # u = x-1, v = x+1
+ # _ _
+ # Taylor: | u 1 u^3 1 u^5 |
+ # ln (x) = 2 | --- + - * --- + - * --- + ... | x > 0
+ # |_ v 3 v^3 5 v^5 _|
+
+ # This takes much more steps to calculate the result:
+ # u = x-1
+ # _ _
+ # Taylor: | u 1 u^2 1 u^3 |
+ # ln (x) = 2 | --- + - * --- + - * --- + ... | x > 1/2
+ # |_ x 2 x^2 3 x^3 _|
+
+ # we need to limit the accuracy to protect against overflow
+ my $fallback = 0;
+ my $scale = 0;
+ my @params = $x->_find_round_parameters($a,$p,$r);
+
+ # no rounding at all, so must use fallback
+ if (scalar @params == 1)
+ {
+ # simulate old behaviour
+ $params[1] = $self->div_scale(); # and round to it as accuracy
+ $scale = $params[1]+4; # at least four more for proper round
+ $params[3] = $r; # round mode by caller or undef
+ $fallback = 1; # to clear a/p afterwards
+ }
+ else
+ {
+ # the 4 below is empirical, and there might be cases where it is not
+ # enough...
+ $scale = abs($params[1] || $params[2]) + 4; # take whatever is defined
+ }
+
+ return $x->bzero(@params) if $x->is_one();
+ return $x->bnan() if $x->{sign} ne '+' || $x->is_zero();
+ #return $x->bone('+',@params) if $x->bcmp($base) == 0;
+
+ # when user set globals, they would interfere with our calculation, so
+ # disable then and later re-enable them
+ no strict 'refs';
+ my $abr = "$self\::accuracy"; my $ab = $$abr; $$abr = undef;
+ my $pbr = "$self\::precision"; my $pb = $$pbr; $$pbr = undef;
+ # we also need to disable any set A or P on $x (_find_round_parameters took
+ # them already into account), since these would interfere, too
+ delete $x->{_a}; delete $x->{_p};
+ # need to disable $upgrade in BigInt, to avoid deep recursion
+ local $Math::BigInt::upgrade = undef;
+
+ my ($case,$limit,$v,$u,$below,$factor,$two,$next,$over,$f);
+
+ if (3 < 5)
+ #if ($x <= Math::BigFloat->new("0.5"))
+ {
+ $case = 0;
+ # print "case $case $x < 0.5\n";
+ $v = $x->copy(); $v->binc(); # v = x+1
+ $x->bdec(); $u = $x->copy(); # u = x-1; x = x-1
+ $x->bdiv($v,$scale); # first term: u/v
+ $below = $v->copy();
+ $over = $u->copy();
+ $u *= $u; $v *= $v; # u^2, v^2
+ $below->bmul($v); # u^3, v^3
+ $over->bmul($u);
+ $factor = $self->new(3); $f = $self->new(2);
+ }
+ #else
+ # {
+ # $case = 1;
+ # print "case 1 $x > 0.5\n";
+ # $v = $x->copy(); # v = x
+ # $u = $x->copy(); $u->bdec(); # u = x-1;
+ # $x->bdec(); $x->bdiv($v,$scale); # first term: x-1/x
+ # $below = $v->copy();
+ # $over = $u->copy();
+ # $below->bmul($v); # u^2, v^2
+ # $over->bmul($u);
+ # $factor = $self->new(2); $f = $self->bone();
+ # }
+ $limit = $self->new("1E-". ($scale-1));
+ #my $steps = 0;
+ while (3 < 5)
+ {
+ # we calculate the next term, and add it to the last
+ # when the next term is below our limit, it won't affect the outcome
+ # anymore, so we stop
+ $next = $over->copy()->bdiv($below->copy()->bmul($factor),$scale);
+ last if $next->bcmp($limit) <= 0;
+ $x->badd($next);
+ # print "step $x\n";
+ # calculate things for the next term
+ $over *= $u; $below *= $v; $factor->badd($f);
+ #$steps++;
+ }
+ $x->bmul(2) if $case == 0;
+ #print "took $steps steps\n";
+
+ # shortcut to not run trough _find_round_parameters again
+ if (defined $params[1])
+ {
+ $x->bround($params[1],$params[3]); # then round accordingly
+ }
+ else
+ {
+ $x->bfround($params[2],$params[3]); # then round accordingly
+ }
+ if ($fallback)
+ {
+ # clear a/p after round, since user did not request it
+ $x->{_a} = undef; $x->{_p} = undef;
+ }
+ # restore globals
+ $$abr = $ab; $$pbr = $pb;
+
+ $x;
+ }
+
sub blcm
{
- # (BINT or num_str, BINT or num_str) return BINT
+ # (BFLOAT or num_str, BFLOAT or num_str) return BFLOAT
# does not modify arguments, but returns new object
# Lowest Common Multiplicator
sub bgcd
{
- # (BINT or num_str, BINT or num_str) return BINT
+ # (BFLOAT or num_str, BFLOAT or num_str) return BINT
# does not modify arguments, but returns new object
# GCD -- Euclids algorithm Knuth Vol 2 pg 296
$x;
}
+###############################################################################
+# is_foo methods (is_negative, is_positive are inherited from BigInt)
+
+sub is_int
+ {
+ # return true if arg (BFLOAT or num_str) is an integer
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+
+ return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN and +-inf aren't
+ $x->{_e}->{sign} eq '+'; # 1e-1 => no integer
+ 0;
+ }
+
sub is_zero
{
- # return true if arg (BINT or num_str) is zero (array '+', '0')
- my $x = shift; $x = $class->new($x) unless ref $x;
+ # return true if arg (BFLOAT or num_str) is zero
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
return 1 if $x->{sign} eq '+' && $x->{_m}->is_zero();
- return 0;
+ 0;
}
sub is_one
{
- # return true if arg (BINT or num_str) is +1 (array '+', '1')
- # or -1 if signis given
- my $x = shift; $x = $class->new($x) unless ref $x;
- #my ($self,$x) = objectify(1,@_);
- my $sign = $_[2] || '+';
- return ($x->{sign} eq $sign && $x->{_e}->is_zero() && $x->{_m}->is_one());
+ # return true if arg (BFLOAT or num_str) is +1 or -1 if signis given
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+
+ my $sign = shift || ''; $sign = '+' if $sign ne '-';
+ return 1
+ if ($x->{sign} eq $sign && $x->{_e}->is_zero() && $x->{_m}->is_one());
+ 0;
}
sub is_odd
{
- # return true if arg (BINT or num_str) is odd or false if even
- my $x = shift; $x = $class->new($x) unless ref $x;
- #my ($self,$x) = objectify(1,@_);
+ # return true if arg (BFLOAT or num_str) is odd or false if even
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
- return 0 if $x->{sign} !~ /^[+-]$/; # NaN & +-inf aren't
- return ($x->{_e}->is_zero() && $x->{_m}->is_odd());
+ return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN & +-inf aren't
+ ($x->{_e}->is_zero() && $x->{_m}->is_odd());
+ 0;
}
sub is_even
{
# return true if arg (BINT or num_str) is even or false if odd
- my $x = shift; $x = $class->new($x) unless ref $x;
- #my ($self,$x) = objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
return 0 if $x->{sign} !~ /^[+-]$/; # NaN & +-inf aren't
- return 1 if $x->{_m}->is_zero(); # 0e1 is even
- return ($x->{_e}->is_zero() && $x->{_m}->is_even()); # 123.45 is never
+ return 1 if ($x->{_e}->{sign} eq '+' # 123.45 is never
+ && $x->{_m}->is_even()); # but 1200 is
+ 0;
}
sub bmul
# (BINT or num_str, BINT or num_str) return BINT
my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
- # print "mbf bmul $x->{_m}e$x->{_e} $y->{_m}e$y->{_e}\n";
return $x->bnan() if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
- # handle result = 0
- return $x->bzero() if $x->is_zero() || $y->is_zero();
# inf handling
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('-');
}
+ # handle result = 0
+ return $x->bzero() if $x->is_zero() || $y->is_zero();
+
+ return $upgrade->bmul($x,$y,$a,$p,$r) if defined $upgrade &&
+ ((!$x->isa($self)) || (!$y->isa($self)));
# aEb * cEd = (a*c)E(b+d)
- $x->{_m} = $x->{_m} * $y->{_m};
- #print "m: $x->{_m}\n";
- $x->{_e} = $x->{_e} + $y->{_e};
- #print "e: $x->{_m}\n";
+ $x->{_m}->bmul($y->{_m});
+ $x->{_e}->badd($y->{_e});
# adjust sign:
$x->{sign} = $x->{sign} ne $y->{sign} ? '-' : '+';
- #print "s: $x->{sign}\n";
- $x->bnorm();
- return $x->round($a,$p,$r,$y);
+ return $x->bnorm()->round($a,$p,$r,$y);
}
sub bdiv
{
# (dividend: BFLOAT or num_str, divisor: BFLOAT or num_str) return
- # (BFLOAT,BFLOAT) (quo,rem) or BINT (only rem)
+ # (BFLOAT,BFLOAT) (quo,rem) or BFLOAT (only rem)
my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
- # x / +-inf => 0, reminder x
- return wantarray ? ($x->bzero(),$x->copy()) : $x->bzero()
- if $y->{sign} =~ /^[+-]inf$/;
-
- # NaN if x == NaN or y == NaN or x==y==0
- return wantarray ? ($x->bnan(),bnan()) : $x->bnan()
- if (($x->is_nan() || $y->is_nan()) ||
- ($x->is_zero() && $y->is_zero()));
+ return $self->_div_inf($x,$y)
+ if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
- # 5 / 0 => +inf, -6 / 0 => -inf
- return wantarray
- ? ($x->binf($x->{sign}),$self->bnan()) : $x->binf($x->{sign})
- if ($x->{sign} =~ /^[+-]$/ && $y->is_zero());
+ # x== 0 # also: or y == 1 or y == -1
+ return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero();
- $y = $class->new($y) if ref($y) ne $class; # promote bigints
+ # upgrade ?
+ return $upgrade->bdiv($upgrade->new($x),$y,$a,$p,$r) if defined $upgrade;
- # print "mbf bdiv $x ",ref($x)," ",$y," ",ref($y),"\n";
# we need to limit the accuracy to protect against overflow
- my ($scale) = $x->_scale_a($accuracy,$rnd_mode,$a,$r); # ignore $p
my $fallback = 0;
- if (!defined $scale)
+ my $scale = 0;
+ my @params = $x->_find_round_parameters($a,$p,$r,$y);
+
+ # no rounding at all, so must use fallback
+ if (scalar @params == 1)
{
# simulate old behaviour
- $scale = $div_scale+1; # one more for proper riund
- $a = $div_scale; # and round to it
- $fallback = 1; # to clear a/p afterwards
+ $params[1] = $self->div_scale(); # and round to it as accuracy
+ $scale = $params[1]+4; # at least four more for proper round
+ $params[3] = $r; # round mode by caller or undef
+ $fallback = 1; # to clear a/p afterwards
+ }
+ else
+ {
+ # the 4 below is empirical, and there might be cases where it is not
+ # enough...
+ $scale = abs($params[1] || $params[2]) + 4; # take whatever is defined
}
my $lx = $x->{_m}->length(); my $ly = $y->{_m}->length();
$scale = $lx if $lx > $scale;
$scale = $ly if $ly > $scale;
- #print "scale $scale $lx $ly\n";
my $diff = $ly - $lx;
$scale += $diff if $diff > 0; # if lx << ly, but not if ly << lx!
-
- return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero();
+
+ # make copy of $x in case of list context for later reminder calculation
+ my $rem;
+ if (wantarray && !$y->is_one())
+ {
+ $rem = $x->copy();
+ }
$x->{sign} = $x->{sign} ne $y->sign() ? '-' : '+';
# check for / +-1 ( +/- 1E0)
- if ($y->is_one())
- {
- return wantarray ? ($x,$self->bzero()) : $x;
- }
-
- # a * 10 ** b / c * 10 ** d => a/c * 10 ** (b-d)
- #print "self: $self x: $x ref(x) ", ref($x)," m: $x->{_m}\n";
- # my $scale_10 = 10 ** $scale; $x->{_m}->bmul($scale_10);
- $x->{_m}->blsft($scale,10);
- #print "m: $x->{_m} $y->{_m}\n";
- $x->{_m}->bdiv( $y->{_m} ); # a/c
- #print "m: $x->{_m}\n";
- #print "e: $x->{_e} $y->{_e}",$scale,"\n";
- $x->{_e}->bsub($y->{_e}); # b-d
- #print "e: $x->{_e}\n";
- $x->{_e}->bsub($scale); # correct for 10**scale
- #print "after div: m: $x->{_m} e: $x->{_e}\n";
- $x->bnorm(); # remove trailing 0's
- #print "after div: m: $x->{_m} e: $x->{_e}\n";
- $x->round($a,$p,$r); # then round accordingly
+ if (!$y->is_one())
+ {
+ # promote BigInts and it's subclasses (except when already a BigFloat)
+ $y = $self->new($y) unless $y->isa('Math::BigFloat');
+
+ #print "bdiv $y ",ref($y),"\n";
+ # need to disable $upgrade in BigInt, to avoid deep recursion
+ local $Math::BigInt::upgrade = undef; # should be parent class vs MBI
+
+ # calculate the result to $scale digits and then round it
+ # a * 10 ** b / c * 10 ** d => a/c * 10 ** (b-d)
+ $x->{_m}->blsft($scale,10);
+ $x->{_m}->bdiv( $y->{_m} ); # a/c
+ $x->{_e}->bsub( $y->{_e} ); # b-d
+ $x->{_e}->bsub($scale); # correct for 10**scale
+ $x->bnorm(); # remove trailing 0's
+ }
+
+ # shortcut to not run trough _find_round_parameters again
+ if (defined $params[1])
+ {
+ $x->bround($params[1],$params[3]); # then round accordingly
+ }
+ else
+ {
+ $x->bfround($params[2],$params[3]); # then round accordingly
+ }
if ($fallback)
{
# clear a/p after round, since user did not request it
- $x->{_a} = undef;
- $x->{_p} = undef;
+ $x->{_a} = undef; $x->{_p} = undef;
}
if (wantarray)
{
- my $rem = $x->copy();
- $rem->bmod($y,$a,$p,$r);
+ if (!$y->is_one())
+ {
+ $rem->bmod($y,$params[1],$params[2],$params[3]); # copy already done
+ }
+ else
+ {
+ $rem = $self->bzero();
+ }
if ($fallback)
{
# clear a/p after round, since user did not request it
- $x->{_a} = undef;
- $x->{_p} = undef;
+ $rem->{_a} = undef; $rem->{_p} = undef;
}
return ($x,$rem);
}
- return $x;
+ $x;
}
sub bmod
# (dividend: BFLOAT or num_str, divisor: BFLOAT or num_str) return reminder
my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
- return $x->bnan() if ($x->{sign} eq $nan || $y->is_nan() || $y->is_zero());
- return $x->bzero() if $y->is_one();
+ if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
+ {
+ my ($d,$re) = $self->SUPER::_div_inf($x,$y);
+ return $re->round($a,$p,$r,$y);
+ }
+ return $x->bnan() if $x->is_zero() && $y->is_zero();
+ return $x if $y->is_zero();
+ return $x->bnan() if $x->is_nan() || $y->is_nan();
+ return $x->bzero() if $y->is_one() || $x->is_zero();
+
+ # inf handling is missing here
+
+ my $cmp = $x->bacmp($y); # equal or $x < $y?
+ return $x->bzero($a,$p) if $cmp == 0; # $x == $y => result 0
+
+ # only $y of the operands negative?
+ my $neg = 0; $neg = 1 if $x->{sign} ne $y->{sign};
+
+ $x->{sign} = $y->{sign}; # calc sign first
+ return $x->round($a,$p,$r) if $cmp < 0 && $neg == 0; # $x < $y => result $x
+
+ my $ym = $y->{_m}->copy();
+
+ # 2e1 => 20
+ $ym->blsft($y->{_e},10) if $y->{_e}->{sign} eq '+' && !$y->{_e}->is_zero();
+
+ # if $y has digits after dot
+ my $shifty = 0; # correct _e of $x by this
+ if ($y->{_e}->{sign} eq '-') # has digits after dot
+ {
+ # 123 % 2.5 => 1230 % 25 => 5 => 0.5
+ $shifty = $y->{_e}->copy()->babs(); # no more digits after dot
+ $x->blsft($shifty,10); # 123 => 1230, $y->{_m} is already 25
+ }
+ # $ym is now mantissa of $y based on exponent 0
+
+ my $shiftx = 0; # correct _e of $x by this
+ if ($x->{_e}->{sign} eq '-') # has digits after dot
+ {
+ # 123.4 % 20 => 1234 % 200
+ $shiftx = $x->{_e}->copy()->babs(); # no more digits after dot
+ $ym->blsft($shiftx,10);
+ }
+ # 123e1 % 20 => 1230 % 20
+ if ($x->{_e}->{sign} eq '+' && !$x->{_e}->is_zero())
+ {
+ $x->{_m}->blsft($x->{_e},10);
+ }
+ $x->{_e} = $MBI->bzero() unless $x->{_e}->is_zero();
+
+ $x->{_e}->bsub($shiftx) if $shiftx != 0;
+ $x->{_e}->bsub($shifty) if $shifty != 0;
+
+ # now mantissas are equalized, exponent of $x is adjusted, so calc result
+
+ $x->{_m}->bmod($ym);
- # XXX tels: not done yet
- return $x->round($a,$p,$r,$y);
+ $x->{sign} = '+' if $x->{_m}->is_zero(); # fix sign for -0
+ $x->bnorm();
+
+ if ($neg != 0) # one of them negative => correct in place
+ {
+ my $r = $y - $x;
+ $x->{_m} = $r->{_m};
+ $x->{_e} = $r->{_e};
+ $x->{sign} = '+' if $x->{_m}->is_zero(); # fix sign for -0
+ $x->bnorm();
+ }
+
+ $x->round($a,$p,$r,$y); # round and return
}
sub bsqrt
{
# calculate square root; this should probably
# use a different test to see whether the accuracy we want is...
- my ($self,$x,$a,$p,$r) = objectify(1,@_);
+ my ($self,$x,$a,$p,$r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
return $x->bnan() if $x->{sign} eq 'NaN' || $x->{sign} =~ /^-/; # <0, NaN
return $x if $x->{sign} eq '+inf'; # +inf
- return $x if $x->is_zero() || $x == 1;
+ return $x if $x->is_zero() || $x->is_one();
# we need to limit the accuracy to protect against overflow
- my ($scale) = $x->_scale_a($accuracy,$rnd_mode,$a,$r); # ignore $p
my $fallback = 0;
- if (!defined $scale)
+ my $scale = 0;
+ my @params = $x->_find_round_parameters($a,$p,$r);
+
+ # no rounding at all, so must use fallback
+ if (scalar @params == 1)
{
# simulate old behaviour
- $scale = $div_scale+1; # one more for proper riund
- $a = $div_scale; # and round to it
- $fallback = 1; # to clear a/p afterwards
+ $params[1] = $self->div_scale(); # and round to it as accuracy
+ $scale = $params[1]+4; # at least four more for proper round
+ $params[3] = $r; # round mode by caller or undef
+ $fallback = 1; # to clear a/p afterwards
+ }
+ else
+ {
+ # the 4 below is empirical, and there might be cases where it is not
+ # enough...
+ $scale = abs($params[1] || $params[2]) + 4; # take whatever is defined
+ }
+
+ # when user set globals, they would interfere with our calculation, so
+ # disable them and later re-enable them
+ no strict 'refs';
+ my $abr = "$self\::accuracy"; my $ab = $$abr; $$abr = undef;
+ my $pbr = "$self\::precision"; my $pb = $$pbr; $$pbr = undef;
+ # we also need to disable any set A or P on $x (_find_round_parameters took
+ # them already into account), since these would interfere, too
+ delete $x->{_a}; delete $x->{_p};
+ # need to disable $upgrade in BigInt, to avoid deep recursion
+ local $Math::BigInt::upgrade = undef; # should be really parent class vs MBI
+
+ my $xas = $x->as_number();
+ my $gs = $xas->copy()->bsqrt(); # some guess
+
+# print "guess $gs\n";
+ if (($x->{_e}->{sign} ne '-') # guess can't be accurate if there are
+ # digits after the dot
+ && ($xas->bacmp($gs * $gs) == 0)) # guess hit the nail on the head?
+ {
+ # exact result
+ $x->{_m} = $gs; $x->{_e} = $MBI->bzero(); $x->bnorm();
+ # shortcut to not run trough _find_round_parameters again
+ if (defined $params[1])
+ {
+ $x->bround($params[1],$params[3]); # then round accordingly
+ }
+ else
+ {
+ $x->bfround($params[2],$params[3]); # then round accordingly
+ }
+ if ($fallback)
+ {
+ # clear a/p after round, since user did not request it
+ $x->{_a} = undef; $x->{_p} = undef;
+ }
+ # re-enable A and P, upgrade is taken care of by "local"
+ ${"$self\::accuracy"} = $ab; ${"$self\::precision"} = $pb;
+ return $x;
}
+ $gs = $self->new( $gs ); # BigInt to BigFloat
+
my $lx = $x->{_m}->length();
$scale = $lx if $scale < $lx;
- my $e = Math::BigFloat->new("1E-$scale"); # make test variable
- return $x->bnan() if $e->sign() eq 'NaN';
-
- # start with some reasonable guess
- #$x *= 10 ** ($len - $org->{_e}); $x /= 2; # !?!?
- $lx = $lx+$x->{_e};
- $lx = 1 if $lx < 1;
- my $gs = Math::BigFloat->new('1'. ('0' x $lx));
+ my $e = $self->new("1E-$scale"); # make test variable
+
+ my $y = $x->copy();
+ my $two = $self->new(2);
+ my $diff = $e;
+ # promote BigInts and it's subclasses (except when already a BigFloat)
+ $y = $self->new($y) unless $y->isa('Math::BigFloat');
+
+ my $rem;
+ while ($diff->bacmp($e) >= 0)
+ {
+ $rem = $y->copy()->bdiv($gs,$scale);
+ $rem = $y->copy()->bdiv($gs,$scale)->badd($gs)->bdiv($two,$scale);
+ $diff = $rem->copy()->bsub($gs);
+ $gs = $rem->copy();
+ }
+ # copy over to modify $x
+ $x->{_m} = $rem->{_m}; $x->{_e} = $rem->{_e};
- # print "first guess: $gs (x $x) scale $scale\n";
+ # shortcut to not run trough _find_round_parameters again
+ if (defined $params[1])
+ {
+ $x->bround($params[1],$params[3]); # then round accordingly
+ }
+ else
+ {
+ $x->bfround($params[2],$params[3]); # then round accordingly
+ }
+ if ($fallback)
+ {
+ # clear a/p after round, since user did not request it
+ $x->{_a} = undef; $x->{_p} = undef;
+ }
+ # restore globals
+ $$abr = $ab; $$pbr = $pb;
+ $x;
+ }
+
+sub bfac
+ {
+ # (BFLOAT or num_str, BFLOAT or num_str) return BFLOAT
+ # compute factorial numbers
+ # modifies first argument
+ my ($self,$x,@r) = objectify(1,@_);
+
+ return $x->bnan()
+ if (($x->{sign} ne '+') || # inf, NaN, <0 etc => NaN
+ ($x->{_e}->{sign} ne '+')); # digits after dot?
+
+ return $x->bone(@r) if $x->is_zero() || $x->is_one(); # 0 or 1 => 1
+
+ # use BigInt's bfac() for faster calc
+ $x->{_m}->blsft($x->{_e},10); # un-norm m
+ $x->{_e}->bzero(); # norm $x again
+ $x->{_m}->bfac(); # factorial
+ $x->bnorm()->round(@r);
+ }
+
+sub _pow2
+ {
+ # Calculate a power where $y is a non-integer, like 2 ** 0.5
+ my ($x,$y,$a,$p,$r) = @_;
+ my $self = ref($x);
+
+ # we need to limit the accuracy to protect against overflow
+ my $fallback = 0;
+ my $scale = 0;
+ my @params = $x->_find_round_parameters($a,$p,$r);
+
+ # no rounding at all, so must use fallback
+ if (scalar @params == 1)
+ {
+ # simulate old behaviour
+ $params[1] = $self->div_scale(); # and round to it as accuracy
+ $scale = $params[1]+4; # at least four more for proper round
+ $params[3] = $r; # round mode by caller or undef
+ $fallback = 1; # to clear a/p afterwards
+ }
+ else
+ {
+ # the 4 below is empirical, and there might be cases where it is not
+ # enough...
+ $scale = abs($params[1] || $params[2]) + 4; # take whatever is defined
+ }
+
+ # when user set globals, they would interfere with our calculation, so
+ # disable then and later re-enable them
+ no strict 'refs';
+ my $abr = "$self\::accuracy"; my $ab = $$abr; $$abr = undef;
+ my $pbr = "$self\::precision"; my $pb = $$pbr; $$pbr = undef;
+ # we also need to disable any set A or P on $x (_find_round_parameters took
+ # them already into account), since these would interfere, too
+ delete $x->{_a}; delete $x->{_p};
+ # need to disable $upgrade in BigInt, to avoid deep recursion
+ local $Math::BigInt::upgrade = undef;
- my $diff = $e;
- my $y = $x->copy();
- my $two = Math::BigFloat->new(2);
- $x = Math::BigFloat->new($x) if ref($x) ne $class; # promote BigInts
- # $scale = 2;
- while ($diff >= $e)
- {
- return $x->bnan() if $gs->is_zero();
- $r = $y->copy(); $r->bdiv($gs,$scale);
- $x = ($r + $gs);
- $x->bdiv($two,$scale);
- $diff = $x->copy()->bsub($gs)->babs();
- $gs = $x->copy();
+ # split the second argument into its integer and fraction part
+ # we calculate the result then from these two parts, like in
+ # 2 ** 2.4 == (2 ** 2) * (2 ** 0.4)
+ my $c = $self->new($y->as_number()); # integer part
+ my $d = $y-$c; # fractional part
+ my $xc = $x->copy(); # a temp. copy
+
+ # now calculate binary fraction from the decimal fraction on the fly
+ # f.i. 0.654:
+ # 0.654 * 2 = 1.308 > 1 => 0.1 ( 1.308 - 1 = 0.308)
+ # 0.308 * 2 = 0.616 < 1 => 0.10
+ # 0.616 * 2 = 1.232 > 1 => 0.101 ( 1.232 - 1 = 0.232)
+ # and so on...
+ # The process stops when the result is exactly one, or when we have
+ # enough accuracy
+
+ # From the binary fraction we calculate the result as follows:
+ # we assume the fraction ends in 1, and we remove this one first.
+ # For each digit after the dot, assume 1 eq R and 0 eq XR, where R means
+ # take square root and X multiply with the original X.
+
+ my $i = 0;
+ while ($i++ < 50)
+ {
+ $d->badd($d); # * 2
+ last if $d->is_one(); # == 1
+ $x->bsqrt(); # 0
+ if ($d > 1)
+ {
+ $x->bsqrt(); $x->bmul($xc); $d->bdec(); # 1
+ }
+ }
+ # assume fraction ends in 1
+ $x->bsqrt(); # 1
+ if (!$c->is_one())
+ {
+ $x->bmul( $xc->bpow($c) );
+ }
+ elsif (!$c->is_zero())
+ {
+ $x->bmul( $xc );
+ }
+ # done
+
+ # shortcut to not run trough _find_round_parameters again
+ if (defined $params[1])
+ {
+ $x->bround($params[1],$params[3]); # then round accordingly
+ }
+ else
+ {
+ $x->bfround($params[2],$params[3]); # then round accordingly
}
- $x->round($a,$p,$r);
if ($fallback)
{
# clear a/p after round, since user did not request it
- $x->{_a} = undef;
- $x->{_p} = undef;
+ $x->{_a} = undef; $x->{_p} = undef;
}
+ # restore globals
+ $$abr = $ab; $$pbr = $pb;
+ $x;
+ }
+
+sub _pow
+ {
+ # Calculate a power where $y is a non-integer, like 2 ** 0.5
+ my ($x,$y,$a,$p,$r) = @_;
+ my $self = ref($x);
+
+ # if $y == 0.5, it is sqrt($x)
+ return $x->bsqrt($a,$p,$r,$y) if $y->bcmp('0.5') == 0;
+
+ # u = y * ln x
+ # _ _
+ # Taylor: | u u^2 u^3 |
+ # x ** y = 1 + | --- + --- + * ----- + ... |
+ # |_ 1 1*2 1*2*3 _|
+
+ # we need to limit the accuracy to protect against overflow
+ my $fallback = 0;
+ my $scale = 0;
+ my @params = $x->_find_round_parameters($a,$p,$r);
+
+ # no rounding at all, so must use fallback
+ if (scalar @params == 1)
+ {
+ # simulate old behaviour
+ $params[1] = $self->div_scale(); # and round to it as accuracy
+ $scale = $params[1]+4; # at least four more for proper round
+ $params[3] = $r; # round mode by caller or undef
+ $fallback = 1; # to clear a/p afterwards
+ }
+ else
+ {
+ # the 4 below is empirical, and there might be cases where it is not
+ # enough...
+ $scale = abs($params[1] || $params[2]) + 4; # take whatever is defined
+ }
+
+ # when user set globals, they would interfere with our calculation, so
+ # disable then and later re-enable them
+ no strict 'refs';
+ my $abr = "$self\::accuracy"; my $ab = $$abr; $$abr = undef;
+ my $pbr = "$self\::precision"; my $pb = $$pbr; $$pbr = undef;
+ # we also need to disable any set A or P on $x (_find_round_parameters took
+ # them already into account), since these would interfere, too
+ delete $x->{_a}; delete $x->{_p};
+ # need to disable $upgrade in BigInt, to avoid deep recursion
+ local $Math::BigInt::upgrade = undef;
+
+ my ($limit,$v,$u,$below,$factor,$next,$over);
+
+ $u = $x->copy()->blog($scale)->bmul($y);
+ $v = $self->bone(); # 1
+ $factor = $self->new(2); # 2
+ $x->bone(); # first term: 1
+
+ $below = $v->copy();
+ $over = $u->copy();
+
+ $limit = $self->new("1E-". ($scale-1));
+ #my $steps = 0;
+ while (3 < 5)
+ {
+ # we calculate the next term, and add it to the last
+ # when the next term is below our limit, it won't affect the outcome
+ # anymore, so we stop
+ $next = $over->copy()->bdiv($below,$scale);
+ last if $next->bcmp($limit) <= 0;
+ $x->badd($next);
+# print "at $x\n";
+ # calculate things for the next term
+ $over *= $u; $below *= $factor; $factor->binc();
+ #$steps++;
+ }
+
+ # shortcut to not run trough _find_round_parameters again
+ if (defined $params[1])
+ {
+ $x->bround($params[1],$params[3]); # then round accordingly
+ }
+ else
+ {
+ $x->bfround($params[2],$params[3]); # then round accordingly
+ }
+ if ($fallback)
+ {
+ # clear a/p after round, since user did not request it
+ $x->{_a} = undef; $x->{_p} = undef;
+ }
+ # restore globals
+ $$abr = $ab; $$pbr = $pb;
$x;
}
return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan;
return $x->bone() if $y->is_zero();
return $x if $x->is_one() || $y->is_one();
+
+ return $x->_pow($y,$a,$p,$r) if !$y->is_int(); # non-integer power
+
my $y1 = $y->as_number(); # make bigint
- if ($x == -1)
+ # if ($x == -1)
+ if ($x->{sign} eq '-' && $x->{_m}->is_one() && $x->{_e}->is_zero())
{
# if $x == -1 and odd/even y => +1/-1 because +-1 ^ (+-1) => +-1
return $y1->is_odd() ? $x : $x->babs(1);
}
- return $x if $x->is_zero() && $y->{sign} eq '+'; # 0**y => 0 (if not y <= 0)
- # 0 ** -y => 1 / (0 ** y) => / 0! (1 / 0 => +inf)
- return $x->binf() if $x->is_zero() && $y->{sign} eq '-';
+ if ($x->is_zero())
+ {
+ return $x if $y->{sign} eq '+'; # 0**y => 0 (if not y <= 0)
+ # 0 ** -y => 1 / (0 ** y) => / 0! (1 / 0 => +inf)
+ $x->binf();
+ }
# calculate $x->{_m} ** $y and $x->{_e} * $y separately (faster)
$y1->babs();
my $z = $x->copy(); $x->bzero()->binc();
return $x->bdiv($z,$a,$p,$r); # round in one go (might ignore y's A!)
}
- return $x->round($a,$p,$r,$y);
+ $x->round($a,$p,$r,$y);
}
###############################################################################
# precision: round to the $Nth digit left (+$n) or right (-$n) from the '.'
# $n == 0 means round to integer
# expects and returns normalized numbers!
- my $x = shift; $x = $class->new($x) unless ref $x;
+ my $x = shift; my $self = ref($x) || $x; $x = $self->new(shift) if !ref($x);
return $x if $x->modify('bfround');
- my ($scale,$mode) = $x->_scale_p($precision,$rnd_mode,@_);
+ my ($scale,$mode) = $x->_scale_p($self->precision(),$self->round_mode(),@_);
return $x if !defined $scale; # no-op
# never round a 0, +-inf, NaN
- return $x if $x->{sign} !~ /^[+-]$/ || $x->is_zero();
+ if ($x->is_zero())
+ {
+ $x->{_p} = $scale if !defined $x->{_p} || $x->{_p} < $scale; # -3 < -2
+ return $x;
+ }
+ return $x if $x->{sign} !~ /^[+-]$/;
# print "MBF bfround $x to scale $scale mode $mode\n";
+ # don't round if x already has lower precision
+ return $x if (defined $x->{_p} && $x->{_p} < 0 && $scale < $x->{_p});
+
+ $x->{_p} = $scale; # remember round in any case
+ $x->{_a} = undef; # and clear A
if ($scale < 0)
{
# print "bfround scale $scale e $x->{_e}\n";
my $dad = -$x->{_e}; # digits after dot
my $zad = 0; # zeros after dot
$zad = -$len-$x->{_e} if ($x->{_e} < -$len);# for 0.00..00xxx style
- # print "scale $scale dad $dad zad $zad len $len\n";
+ #print "scale $scale dad $dad zad $zad len $len\n";
# number bsstr len zad dad
# 0.123 123e-3 3 0 3
# do not round after/right of the $dad
return $x if $scale > $dad; # 0.123, scale >= 3 => exit
- # round to zero if rounding inside the $zad, but not for last zero like:
- # 0.0065, scale -2, round last '0' with following '65' (scale == zad case)
- if ($scale < $zad)
- {
- return $x->bzero();
- }
- if ($scale == $zad) # for 0.006, scale -2 and trunc
+ # round to zero if rounding inside the $zad, but not for last zero like:
+ # 0.0065, scale -2, round last '0' with following '65' (scale == zad case)
+ return $x->bzero() if $scale < $zad;
+ if ($scale == $zad) # for 0.006, scale -3 and trunc
{
$scale = -$len;
}
{
# 123 => 100 means length(123) = 3 - $scale (2) => 1
- # calculate digits before dot
- my $dbt = $x->{_m}->length(); $dbt += $x->{_e} if $x->{_e}->sign() eq '-';
- if (($scale > $dbt) && ($dbt < 0))
- {
- # if not enough digits before dot, round to zero
- return $x->bzero();
- }
- if (($scale >= 0) && ($dbt == 0))
- {
- # 0.49->bfround(1): scale == 1, dbt == 0: => 0.0
- # 0.51->bfround(0): scale == 0, dbt == 0: => 1.0
- # 0.5->bfround(0): scale == 0, dbt == 0: => 0
- # 0.05->bfround(0): scale == 0, dbt == 0: => 0
- # print "$scale $dbt $x->{_m}\n";
- $scale = -$x->{_m}->length();
- }
- elsif ($dbt > 0)
- {
- # correct by subtracting scale
- $scale = $dbt - $scale;
- }
+ my $dbt = $x->{_m}->length();
+ # digits before dot
+ my $dbd = $dbt + $x->{_e};
+ # should be the same, so treat it as this
+ $scale = 1 if $scale == 0;
+ # shortcut if already integer
+ return $x if $scale == 1 && $dbt <= $dbd;
+ # maximum digits before dot
+ ++$dbd;
+
+ if ($scale > $dbd)
+ {
+ # not enough digits before dot, so round to zero
+ return $x->bzero;
+ }
+ elsif ( $scale == $dbd )
+ {
+ # maximum
+ $scale = -$dbt;
+ }
else
- {
- $scale = $x->{_m}->length() - $scale;
- }
+ {
+ $scale = $dbd - $scale;
+ }
+
}
# print "using $scale for $x->{_m} with '$mode'\n";
# pass sign to bround for rounding modes '+inf' and '-inf'
sub bround
{
# accuracy: preserve $N digits, and overwrite the rest with 0's
- my $x = shift; $x = $class->new($x) unless ref $x;
- my ($scale,$mode) = $x->_scale_a($accuracy,$rnd_mode,@_);
- return $x if !defined $scale; # no-op
+ my $x = shift; my $self = ref($x) || $x; $x = $self->new(shift) if !ref($x);
+
+ die ('bround() needs positive accuracy') if ($_[0] || 0) < 0;
+
+ my ($scale,$mode) = $x->_scale_a($self->accuracy(),$self->round_mode(),@_);
+ return $x if !defined $scale; # no-op
return $x if $x->modify('bround');
- # print "bround $scale $mode\n";
- # 0 => return all digits, scale < 0 makes no sense
- return $x if ($scale <= 0);
- # never round a 0, +-inf, NaN
- return $x if $x->{sign} !~ /^[+-]$/ || $x->is_zero();
-
- # if $e longer than $m, we have 0.0000xxxyyy style number, and must
- # subtract the delta from scale, to simulate keeping the zeros
- # -5 +5 => 1; -10 +5 => -4
- my $delta = $x->{_e} + $x->{_m}->length() + 1;
- # removed by tlr, since causes problems with fraction tests:
- # $scale += $delta if $delta < 0;
-
- # if we should keep more digits than the mantissa has, do nothing
- return $x if $x->{_m}->length() <= $scale;
+ # scale is now either $x->{_a}, $accuracy, or the user parameter
+ # test whether $x already has lower accuracy, do nothing in this case
+ # but do round if the accuracy is the same, since a math operation might
+ # want to round a number with A=5 to 5 digits afterwards again
+ return $x if defined $_[0] && defined $x->{_a} && $x->{_a} < $_[0];
+
+ # scale < 0 makes no sense
+ # never round a +-inf, NaN
+ return $x if ($scale < 0) || $x->{sign} !~ /^[+-]$/;
+
+ # 1: $scale == 0 => keep all digits
+ # 2: never round a 0
+ # 3: if we should keep more digits than the mantissa has, do nothing
+ if ($scale == 0 || $x->is_zero() || $x->{_m}->length() <= $scale)
+ {
+ $x->{_a} = $scale if !defined $x->{_a} || $x->{_a} > $scale;
+ return $x;
+ }
# pass sign to bround for '+inf' and '-inf' rounding modes
$x->{_m}->{sign} = $x->{sign};
$x->{_m}->bround($scale,$mode); # round mantissa
$x->{_m}->{sign} = '+'; # fix sign back
+ # $x->{_m}->{_a} = undef; $x->{_m}->{_p} = undef;
+ $x->{_a} = $scale; # remember rounding
+ $x->{_p} = undef; # and clear P
$x->bnorm(); # del trailing zeros gen. by bround()
}
sub bfloor
{
# return integer less or equal then $x
- my ($self,$x,$a,$p,$r) = objectify(1,@_);
+ my ($self,$x,$a,$p,$r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
return $x if $x->modify('bfloor');
# if $x has digits after dot
if ($x->{_e}->{sign} eq '-')
{
- $x->{_m}->brsft(-$x->{_e},10);
- $x->{_e}->bzero();
- $x-- if $x->{sign} eq '-';
+ #$x->{_m}->brsft(-$x->{_e},10);
+ #$x->{_e}->bzero();
+ #$x-- if $x->{sign} eq '-';
+
+ $x->{_e}->{sign} = '+'; # negate e
+ $x->{_m}->brsft($x->{_e},10); # cut off digits after dot
+ $x->{_e}->bzero(); # trunc/norm
+ $x->{_m}->binc() if $x->{sign} eq '-'; # decrement if negative
}
- return $x->round($a,$p,$r);
+ $x->round($a,$p,$r);
}
sub bceil
{
# return integer greater or equal then $x
- my ($self,$x,$a,$p,$r) = objectify(1,@_);
+ my ($self,$x,$a,$p,$r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
return $x if $x->modify('bceil');
return $x if $x->{sign} !~ /^[+-]$/; # nan, +inf, -inf
# if $x has digits after dot
if ($x->{_e}->{sign} eq '-')
{
- $x->{_m}->brsft(-$x->{_e},10);
- $x->{_e}->bzero();
- $x++ if $x->{sign} eq '+';
+ #$x->{_m}->brsft(-$x->{_e},10);
+ #$x->{_e}->bzero();
+ #$x++ if $x->{sign} eq '+';
+
+ $x->{_e}->{sign} = '+'; # negate e
+ $x->{_m}->brsft($x->{_e},10); # cut off digits after dot
+ $x->{_e}->bzero(); # trunc/norm
+ $x->{_m}->binc() if $x->{sign} eq '+'; # decrement if negative
}
- return $x->round($a,$p,$r);
+ $x->round($a,$p,$r);
+ }
+
+sub brsft
+ {
+ # shift right by $y (divide by power of 2)
+ my ($self,$x,$y,$n,$a,$p,$r) = objectify(2,@_);
+
+ return $x if $x->modify('brsft');
+ return $x if $x->{sign} !~ /^[+-]$/; # nan, +inf, -inf
+
+ $n = 2 if !defined $n; $n = Math::BigFloat->new($n);
+ $x->bdiv($n ** $y,$a,$p,$r,$y);
+ }
+
+sub blsft
+ {
+ # shift right by $y (divide by power of 2)
+ my ($self,$x,$y,$n,$a,$p,$r) = objectify(2,@_);
+
+ return $x if $x->modify('brsft');
+ return $x if $x->{sign} !~ /^[+-]$/; # nan, +inf, -inf
+
+ $n = 2 if !defined $n; $n = Math::BigFloat->new($n);
+ $x->bmul($n ** $y,$a,$p,$r,$y);
}
###############################################################################
sub DESTROY
{
- # going trough AUTOLOAD for every DESTROY is costly, so avoid it by empty sub
+ # going through AUTOLOAD for every DESTROY is costly, so avoid it by empty sub
}
sub AUTOLOAD
{
- # make fxxx and bxxx work
- # my $self = $_[0];
+ # make fxxx and bxxx both work by selectively mapping fxxx() to MBF::bxxx()
+ # or falling back to MBI::bxxx()
my $name = $AUTOLOAD;
$name =~ s/.*:://; # split package
- #print "$name\n";
- if (!method_valid($name))
+ no strict 'refs';
+ if (!method_alias($name))
{
- #no strict 'refs';
- ## try one level up
- #&{$class."::SUPER->$name"}(@_);
- # delayed load of Carp and avoid recursion
- require Carp;
- Carp::croak ("Can't call $class\-\>$name, not a valid method");
+ if (!defined $name)
+ {
+ # delayed load of Carp and avoid recursion
+ require Carp;
+ Carp::croak ("Can't call a method without name");
+ }
+ if (!method_hand_up($name))
+ {
+ # delayed load of Carp and avoid recursion
+ require Carp;
+ Carp::croak ("Can't call $class\-\>$name, not a valid method");
+ }
+ # try one level up, but subst. bxxx() for fxxx() since MBI only got bxxx()
+ $name =~ s/^f/b/;
+ return &{"$MBI"."::$name"}(@_);
}
- no strict 'refs';
my $bname = $name; $bname =~ s/^f/b/;
- *{$class."\:\:$name"} = \&$bname;
+ *{$class."::$name"} = \&$bname;
&$bname; # uses @_
}
sub exponent
{
# return a copy of the exponent
- my $self = shift;
- $self = $class->new($self) unless ref $self;
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
- return bnan() if $self->is_nan();
- return $self->{_e}->copy();
+ if ($x->{sign} !~ /^[+-]$/)
+ {
+ my $s = $x->{sign}; $s =~ s/^[+-]//;
+ return $self->new($s); # -inf, +inf => +inf
+ }
+ return $x->{_e}->copy();
}
sub mantissa
{
# return a copy of the mantissa
- my $self = shift;
- $self = $class->new($self) unless ref $self;
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
- return bnan() if $self->is_nan();
- my $m = $self->{_m}->copy(); # faster than going via bstr()
- $m->bneg() if $self->{sign} eq '-';
+ if ($x->{sign} !~ /^[+-]$/)
+ {
+ my $s = $x->{sign}; $s =~ s/^[+]//;
+ return $self->new($s); # -inf, +inf => +inf
+ }
+ my $m = $x->{_m}->copy(); # faster than going via bstr()
+ $m->bneg() if $x->{sign} eq '-';
- return $m;
+ $m;
}
sub parts
{
# return a copy of both the exponent and the mantissa
- my $self = shift;
- $self = $class->new($self) unless ref $self;
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
- return (bnan(),bnan()) if $self->is_nan();
- my $m = $self->{_m}->copy(); # faster than going via bstr()
- $m->bneg() if $self->{sign} eq '-';
- return ($m,$self->{_e}->copy());
+ if ($x->{sign} !~ /^[+-]$/)
+ {
+ my $s = $x->{sign}; $s =~ s/^[+]//; my $se = $s; $se =~ s/^[-]//;
+ return ($self->new($s),$self->new($se)); # +inf => inf and -inf,+inf => inf
+ }
+ my $m = $x->{_m}->copy(); # faster than going via bstr()
+ $m->bneg() if $x->{sign} eq '-';
+ return ($m,$x->{_e}->copy());
}
##############################################################################
# private stuff (internal use only)
-sub _one
- {
- # internal speedup, set argument to 1, or create a +/- 1
- my $self = shift; $self = ref($self) if ref($self);
- my $x = {}; bless $x, $self;
- $x->{_m} = Math::BigInt->new(1);
- $x->{_e} = Math::BigInt->new(0);
- $x->{sign} = shift || '+';
- return $x;
- }
-
sub import
{
my $self = shift;
- #print "import $self\n";
- for ( my $i = 0; $i < @_ ; $i++ )
+ my $l = scalar @_;
+ my $lib = ''; my @a;
+ for ( my $i = 0; $i < $l ; $i++)
{
+# print "at $_[$i] (",$_[$i+1]||'undef',")\n";
if ( $_[$i] eq ':constant' )
{
# this rest causes overlord er load to step in
# print "overload @_\n";
overload::constant float => sub { $self->new(shift); };
- splice @_, $i, 1; last;
+ }
+ elsif ($_[$i] eq 'upgrade')
+ {
+ # this causes upgrading
+ $upgrade = $_[$i+1]; # or undef to disable
+ $i++;
+ }
+ elsif ($_[$i] eq 'downgrade')
+ {
+ # this causes downgrading
+ $downgrade = $_[$i+1]; # or undef to disable
+ $i++;
+ }
+ elsif ($_[$i] eq 'lib')
+ {
+ $lib = $_[$i+1] || ''; # default Calc
+ $i++;
+ }
+ elsif ($_[$i] eq 'with')
+ {
+ $MBI = $_[$i+1] || 'Math::BigInt'; # default Math::BigInt
+ $i++;
+ }
+ else
+ {
+ push @a, $_[$i];
+ }
+ }
+# print "mbf @a\n";
+
+ # let use Math::BigInt lib => 'GMP'; use Math::BigFloat; still work
+ my $mbilib = eval { Math::BigInt->config()->{lib} };
+ if ((defined $mbilib) && ($MBI eq 'Math::BigInt'))
+ {
+ # MBI already loaded
+ $MBI->import('lib',"$lib,$mbilib", 'objectify');
+ }
+ else
+ {
+ # MBI not loaded, or with ne "Math::BigInt"
+ $lib .= ",$mbilib" if defined $mbilib;
+
+# my @parts = split /::/, $MBI; # Math::BigInt => Math BigInt
+# my $file = pop @parts; $file .= '.pm'; # BigInt => BigInt.pm
+# $file = File::Spec->catfile (@parts, $file);
+
+ 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().
+ my @parts = split /::/, $MBI; # Math::BigInt => Math BigInt
+ my $file = pop @parts; $file .= '.pm'; # BigInt => BigInt.pm
+ $file = File::Spec->catfile (@parts, $file);
+ eval { require $file; $MBI->import( lib => '$lib', 'objectify' ); }
+ }
+ else
+ {
+ my $rc = "use $MBI lib => '$lib', 'objectify';";
+ eval $rc;
}
}
+ die ("Couldn't load $MBI: $! $@") if $@;
+
# any non :constant stuff is handled by our parent, Exporter
# even if @_ is empty, to give it a chance
- #$self->SUPER::import(@_); # does not work (would call MBI)
- $self->export_to_level(1,$self,@_); # need this instead
+ $self->SUPER::import(@a); # for subclasses
+ $self->export_to_level(1,$self,@a); # need this, too
}
sub bnorm
{
# adjust m and e so that m is smallest possible
# round number according to accuracy and precision settings
- my $x = shift;
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
return $x if $x->{sign} !~ /^[+-]$/; # inf, nan etc
- my $zeros = $x->{_m}->_trailing_zeros(); # correct for trailing zeros
- if ($zeros != 0)
- {
- $x->{_m}->brsft($zeros,10); $x->{_e} += $zeros;
- }
- # for something like 0Ey, set y to 1
- $x->{sign} = '+', $x->{_e}->bzero()->binc() if $x->{_m}->is_zero();
+# if (!$x->{_m}->is_odd())
+# {
+ my $zeros = $x->{_m}->_trailing_zeros(); # correct for trailing zeros
+ if ($zeros != 0)
+ {
+ $x->{_m}->brsft($zeros,10); $x->{_e}->badd($zeros);
+ }
+ # for something like 0Ey, set y to 1, and -0 => +0
+ $x->{sign} = '+', $x->{_e}->bone() if $x->{_m}->is_zero();
+# }
+ # this is to prevent automatically rounding when MBI's globals are set
$x->{_m}->{_f} = MB_NEVER_ROUND;
$x->{_e}->{_f} = MB_NEVER_ROUND;
- return $x; # MBI bnorm is no-op
- }
+ # 'forget' that mantissa was rounded via MBI::bround() in MBF's bfround()
+ $x->{_m}->{_a} = undef; $x->{_e}->{_a} = undef;
+ $x->{_m}->{_p} = undef; $x->{_e}->{_p} = undef;
+ $x; # MBI bnorm is no-op, so dont call it
+ }
##############################################################################
# internal calculation routines
sub as_number
{
- # return a bigint representation of this BigFloat number
- my ($self,$x) = objectify(1,@_);
+ # return copy as a bigint representation of this BigFloat number
+ my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
- my $z;
- if ($x->{_e}->is_zero())
+ my $z = $x->{_m}->copy();
+ if ($x->{_e}->{sign} eq '-') # < 0
{
- $z = $x->{_m}->copy();
- $z->{sign} = $x->{sign};
- return $z;
- }
- $z = $x->{_m}->copy();
- if ($x->{_e} < 0)
- {
- $z->brsft(-$x->{_e},10);
+ $x->{_e}->{sign} = '+'; # flip
+ $z->brsft($x->{_e},10);
+ $x->{_e}->{sign} = '-'; # flip back
}
- else
+ elsif (!$x->{_e}->is_zero()) # > 0
{
$z->blsft($x->{_e},10);
}
$z->{sign} = $x->{sign};
- return $z;
+ $z;
}
sub length
{
- my $x = shift; $x = $class->new($x) unless ref $x;
+ my $x = shift;
+ my $class = ref($x) || $x;
+ $x = $class->new(shift) unless ref($x);
+ return 1 if $x->{_m}->is_zero();
my $len = $x->{_m}->length();
$len += $x->{_e} if $x->{_e}->sign() eq '+';
if (wantarray())
{
- my $t = Math::BigInt::bzero();
+ my $t = $MBI->bzero();
$t = $x->{_e}->copy()->babs() if $x->{_e}->sign() eq '-';
return ($len,$t);
}
- return $len;
+ $len;
}
1;
use Math::BigFloat;
- # Number creation
- $x = Math::BigInt->new($str); # defaults to 0
- $nan = Math::BigInt->bnan(); # create a NotANumber
- $zero = Math::BigInt->bzero();# create a "+0"
+ # Number creation
+ $x = Math::BigFloat->new($str); # defaults to 0
+ $nan = Math::BigFloat->bnan(); # create a NotANumber
+ $zero = Math::BigFloat->bzero(); # create a +0
+ $inf = Math::BigFloat->binf(); # create a +inf
+ $inf = Math::BigFloat->binf('-'); # create a -inf
+ $one = Math::BigFloat->bone(); # create a +1
+ $one = Math::BigFloat->bone('-'); # create a -1
# Testing
- $x->is_zero(); # return whether arg is zero or not
- $x->is_nan(); # return whether arg is NaN or not
+ $x->is_zero(); # true if arg is +0
+ $x->is_nan(); # true if arg is NaN
$x->is_one(); # true if arg is +1
$x->is_one('-'); # true if arg is -1
$x->is_odd(); # true if odd, false for even
$x->is_even(); # true if even, false for odd
$x->is_positive(); # true if >= 0
$x->is_negative(); # true if < 0
- $x->is_inf(sign) # true if +inf or -inf (sign default '+')
+ $x->is_inf(sign); # true if +inf, or -inf (default is '+')
+
$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
+ $x->digit($n); # return the nth digit, counting from right
+ $x->digit(-$n); # return the nth digit, counting from left
# The following all modify their first argument:
-
+
# set
$x->bzero(); # set $i to 0
$x->bnan(); # set $i to NaN
+ $x->bone(); # set $x to +1
+ $x->bone('-'); # set $x to -1
+ $x->binf(); # set $x to inf
+ $x->binf('-'); # set $x to -inf
$x->bneg(); # negation
$x->babs(); # absolute value
$x->brsft($y); # right shift
# return (quo,rem) or quo if scalar
+ $x->blog($base); # logarithm of $x, base defaults to e
+ # (other bases than e not supported yet)
+
$x->band($y); # bit-wise and
$x->bior($y); # bit-wise inclusive or
$x->bxor($y); # bit-wise exclusive or
$x->bnot(); # bit-wise not (two's complement)
-
+
+ $x->bsqrt(); # calculate square-root
+ $x->bfac(); # factorial of $x (1*2*3*4*..$x)
+
$x->bround($N); # accuracy: preserver $N digits
$x->bfround($N); # precision: round to the $Nth digit
# The following do not modify their arguments:
-
bgcd(@values); # greatest common divisor
blcm(@values); # lowest common multiplicator
$x->bstr(); # return string
$x->bsstr(); # return string in scientific notation
-
+
+ $x->bfloor(); # return integer less or equal than $x
+ $x->bceil(); # return integer greater or equal than $x
+
$x->exponent(); # return exponent as BigInt
$x->mantissa(); # return mantissa as BigInt
$x->parts(); # return (mantissa,exponent) as BigInt
See also: L<Rounding|Rounding>.
-Math::BigFloat supports both precision and accuracy. (here should follow
-a short description of both).
-
-Precision: digits after the '.', laber, schwad
-Accuracy: Significant digits blah blah
+Math::BigFloat supports both precision and accuracy. For a full documentation,
+examples and tips on these topics please see the large section in
+L<Math::BigInt>.
Since things like sqrt(2) or 1/3 must presented with a limited precision lest
a operation consumes all resources, each operation produces no more than
the following: 'even', 'odd', '+inf', '-inf', 'zero' or 'trunc'.
The default rounding mode is 'even'. By using
-C<< Math::BigFloat::round_mode($rnd_mode); >> you can get and set the default
-mode for subsequent rounding. The usage of C<$Math::BigFloat::$rnd_mode> is
+C<< Math::BigFloat::round_mode($round_mode); >> you can get and set the default
+mode for subsequent rounding. The usage of C<$Math::BigFloat::$round_mode> is
no longer supported.
The second parameter to the round functions then overrides the default
temporarily.
perl -MMath::BigFloat=:constant -e 'print 2E-100,"\n"'
-prints the value of C<2E-100>. Note that without conversion of
+prints the value of C<2E-100>. Note that without conversion of
constants the expression 2E-100 will be calculated as normal floating point
number.
+Please note that ':constant' does not affect integer constants, nor binary
+nor hexadecimal constants. Use L<bignum> or L<Math::BigInt> to get this to
+work.
+
+=head2 Math library
+
+Math with the numbers is done (by default) by a module called
+Math::BigInt::Calc. This is equivalent to saying:
+
+ use Math::BigFloat lib => 'Calc';
+
+You can change this by using:
+
+ use Math::BigFloat lib => 'BitVect';
+
+The following would first try to find Math::BigInt::Foo, then
+Math::BigInt::Bar, and when this also fails, revert to Math::BigInt::Calc:
+
+ use Math::BigFloat lib => 'Foo,Math::BigInt::Bar';
+
+Calc.pm uses as internal format an array of elements of some decimal base
+(usually 1e7, but this might be differen for some systems) with the least
+significant digit first, while BitVect.pm uses a bit vector of base 2, most
+significant bit first. Other modules might use even different means of
+representing the numbers. See the respective module documentation for further
+details.
+
+Please note that Math::BigFloat does B<not> use the denoted library itself,
+but it merely passes the lib argument to Math::BigInt. So, instead of the need
+to do:
+
+ use Math::BigInt lib => 'GMP';
+ use Math::BigFloat;
+
+you can roll it all into one line:
+
+ use Math::BigFloat lib => 'GMP';
+
+Use the lib, Luke! And see L<Using Math::BigInt::Lite> for more details.
+
+=head2 Using Math::BigInt::Lite
+
+It is possible to use L<Math::BigInt::Lite> with Math::BigFloat:
+
+ # 1
+ use Math::BigFloat with => 'Math::BigInt::Lite';
+
+There is no need to "use Math::BigInt" or "use Math::BigInt::Lite", but you
+can combine these if you want. For instance, you may want to use
+Math::BigInt objects in your main script, too.
+
+ # 2
+ use Math::BigInt;
+ use Math::BigFloat with => 'Math::BigInt::Lite';
+
+Of course, you can combine this with the C<lib> parameter.
+
+ # 3
+ use Math::BigFloat with => 'Math::BigInt::Lite', lib => 'GMP,Pari';
+
+If you want to use Math::BigInt's, too, simple add a Math::BigInt B<before>:
+
+ # 4
+ use Math::BigInt;
+ use Math::BigFloat with => 'Math::BigInt::Lite', lib => 'GMP,Pari';
+
+Notice that the module with the last C<lib> will "win" and thus
+it's lib will be used if the lib is available:
+
+ # 5
+ use Math::BigInt lib => 'Bar,Baz';
+ use Math::BigFloat with => 'Math::BigInt::Lite', lib => 'Foo';
+
+That would try to load Foo, Bar, Baz and Calc (in that order). Or in other
+words, Math::BigFloat will try to retain previously loaded libs when you
+don't specify it one.
+
+Actually, the lib loading order would be "Bar,Baz,Calc", and then
+"Foo,Bar,Baz,Calc", but independend of which lib exists, the result is the
+same as trying the latter load alone, except for the fact that Bar or Baz
+might be loaded needlessly in an intermidiate step
+
+The old way still works though:
+
+ # 6
+ use Math::BigInt lib => 'Bar,Baz';
+ use Math::BigFloat;
+
+But B<examples #3 and #4 are recommended> for usage.
+
=head1 BUGS
=over 2