📄 bigint.pm
字号:
$scale = ${ $class . '::precision' } unless defined $scale; $mode = ${ $class . '::round_mode' } unless defined $mode; if (defined $scale) { $scale = $scale->can('numify') ? $scale->numify() : "$scale" if ref($scale); $scale = int($scale); } ($scale,$mode); }############################################################################### constructorssub copy { # if two arguments, the first one is the class to "swallow" subclasses if (@_ > 1) { my $self = bless { sign => $_[1]->{sign}, value => $CALC->_copy($_[1]->{value}), }, $_[0] if @_ > 1; $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; }sub new { # create a new BigInt object from a string or another BigInt object. # see hash keys documented at top # the argument could be an object, so avoid ||, && etc on it, this would # cause costly overloaded code to be called. The only allowed ops are # ref() and defined. my ($class,$wanted,$a,$p,$r) = @_; # avoid numify-calls by not using || on $wanted! return $class->bzero($a,$p) if !defined $wanted; # default to 0 return $class->copy($wanted,$a,$p,$r) if ref($wanted) && $wanted->isa($class); # MBI or subclass $class->import() if $IMPORT == 0; # make require work my $self = bless {}, $class; # shortcut for "normal" numbers if ((!ref $wanted) && ($wanted =~ /^([+-]?)[1-9][0-9]*\z/)) { $self->{sign} = $1 || '+'; if ($wanted =~ /^[+-]/) { # remove sign without touching wanted to make it work with constants my $t = $wanted; $t =~ s/^[+-]//; $self->{value} = $CALC->_new($t); } else { $self->{value} = $CALC->_new($wanted); } no strict 'refs'; if ( (defined $a) || (defined $p) || (defined ${"${class}::precision"}) || (defined ${"${class}::accuracy"}) ) { $self->round($a,$p,$r) unless (@_ == 4 && !defined $a && !defined $p); } return $self; } # handle '+inf', '-inf' first if ($wanted =~ /^[+-]?inf\z/) { $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); if (!ref $mis) { if ($_trap_nan) { require Carp; Carp::croak("$wanted is not a number in $class"); } $self->{value} = $CALC->_zero(); $self->{sign} = $nan; return $self; } if (!ref $miv) { # _from_hex or _from_bin $self->{value} = $mis->{value}; $self->{sign} = $mis->{sign}; return $self; # throw away $mis } # make integer from mantissa by adjusting exp, then convert to bigint $self->{sign} = $$mis; # store sign $self->{value} = $CALC->_zero(); # for all the NaN cases my $e = int("$$es$$ev"); # exponent (avoid recursion) if ($e > 0) { my $diff = $e - CORE::length($$mfv); if ($diff < 0) # Not integer { if ($_trap_nan) { require Carp; Carp::croak("$wanted not an integer in $class"); } #print "NOI 1\n"; return $upgrade->new($wanted,$a,$p,$r) if defined $upgrade; $self->{sign} = $nan; } else # diff >= 0 { # adjust fraction and add it to value #print "diff > 0 $$miv\n"; $$miv = $$miv . ($$mfv . '0' x $diff); } } else { if ($$mfv ne '') # e <= 0 { # fraction and negative/zero E => NOI if ($_trap_nan) { require Carp; Carp::croak("$wanted not an integer in $class"); } #print "NOI 2 \$\$mfv '$$mfv'\n"; return $upgrade->new($wanted,$a,$p,$r) if defined $upgrade; $self->{sign} = $nan; } elsif ($e < 0) { # xE-y, and empty mfv #print "xE-y\n"; $e = abs($e); if ($$miv !~ s/0{$e}$//) # can strip so many zero's? { if ($_trap_nan) { require Carp; Carp::croak("$wanted not an integer in $class"); } #print "NOI 3\n"; return $upgrade->new($wanted,$a,$p,$r) if defined $upgrade; $self->{sign} = $nan; } } } $self->{sign} = '+' if $$miv eq '0'; # normalize -0 => +0 $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->round($a,$p,$r) unless @_ == 4 && !defined $a && !defined $p; $self; }sub bnan { # create a bigint 'NaN', if given a BigInt, set it to 'NaN' my $self = shift; $self = $class if !defined $self; if (!ref($self)) { my $c = $self; $self = {}; bless $self, $c; } no strict 'refs'; if (${"${class}::_trap_nan"}) { require Carp; Carp::croak ("Tried to set $self to NaN in $class\::bnan()"); } $self->import() if $IMPORT == 0; # make require work return if $self->modify('bnan'); if ($self->can('_bnan')) { # use subclass to initialize $self->_bnan(); } else { # otherwise do our own thing $self->{value} = $CALC->_zero(); } $self->{sign} = $nan; delete $self->{_a}; delete $self->{_p}; # rounding NaN is silly $self; }sub binf { # create a bigint '+-inf', if given a BigInt, set it to '+-inf' # the sign is either '+', or if given, used from there my $self = shift; my $sign = shift; $sign = '+' if !defined $sign || $sign !~ /^-(inf)?$/; $self = $class if !defined $self; if (!ref($self)) { my $c = $self; $self = {}; bless $self, $c; } no strict 'refs'; if (${"${class}::_trap_inf"}) { require Carp; Carp::croak ("Tried to set $self to +-inf in $class\::binf()"); } $self->import() if $IMPORT == 0; # make require work return if $self->modify('binf'); if ($self->can('_binf')) { # use subclass to initialize $self->_binf(); } else { # otherwise do our own thing $self->{value} = $CALC->_zero(); } $sign = $sign . 'inf' if $sign !~ /inf$/; # - => -inf $self->{sign} = $sign; ($self->{_a},$self->{_p}) = @_; # take over requested rounding $self; }sub bzero { # create a bigint '+0', if given a BigInt, set it to 0 my $self = shift; $self = __PACKAGE__ if !defined $self; if (!ref($self)) { my $c = $self; $self = {}; bless $self, $c; } $self->import() if $IMPORT == 0; # make require work return if $self->modify('bzero'); if ($self->can('_bzero')) { # use subclass to initialize $self->_bzero(); } else { # otherwise do our own thing $self->{value} = $CALC->_zero(); } $self->{sign} = '+'; if (@_ > 0) { if (@_ > 3) { # call like: $x->bzero($a,$p,$r,$y); ($self,$self->{_a},$self->{_p}) = $self->_find_round_parameters(@_); } else { $self->{_a} = $_[0] if ( (!defined $self->{_a}) || (defined $_[0] && $_[0] > $self->{_a})); $self->{_p} = $_[1] if ( (!defined $self->{_p}) || (defined $_[1] && $_[1] > $self->{_p})); } } $self; }sub bone { # create a bigint '+1' (or -1 if given sign '-'), # 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; if (!ref($self)) { my $c = $self; $self = {}; bless $self, $c; } $self->import() if $IMPORT == 0; # make require work return if $self->modify('bone'); if ($self->can('_bone')) { # use subclass to initialize $self->_bone(); } else { # otherwise do our own thing $self->{value} = $CALC->_one(); } $self->{sign} = $sign; if (@_ > 0) { if (@_ > 3) { # call like: $x->bone($sign,$a,$p,$r,$y); ($self,$self->{_a},$self->{_p}) = $self->_find_round_parameters(@_); } else { # call like: $x->bone($sign,$a,$p,$r); $self->{_a} = $_[0] if ( (!defined $self->{_a}) || (defined $_[0] && $_[0] > $self->{_a})); $self->{_p} = $_[1] if ( (!defined $self->{_p}) || (defined $_[1] && $_[1] > $self->{_p})); } } $self; }############################################################################### string conversationsub 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) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); if ($x->{sign} !~ /^[+-]$/) { return $x->{sign} unless $x->{sign} eq '+inf'; # -inf, NaN return 'inf'; # +inf } 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}); }sub bstr { # make a string from bigint object my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); if ($x->{sign} !~ /^[+-]$/) { return $x->{sign} unless $x->{sign} eq '+inf'; # -inf, NaN return 'inf'; # +inf } my $es = ''; $es = $x->{sign} if $x->{sign} eq '-'; $es.$CALC->_str($x->{value}); }sub numify { # Make a "normal" scalar from a BigInt object my $x = shift; $x = $class->new($x) unless ref $x; return $x->bstr() if $x->{sign} !~ /^[+-]$/; my $num = $CALC->_num($x->{value}); return -$num if $x->{sign} eq '-'; $num; }############################################################################### public stuff (usually prefixed with "b")sub sign { # return the sign of the number: +/-/-inf/+inf/NaN my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); $x->{sign}; }sub _find_round_parameters { # After any operation or when calling round(), the result is rounded by # regarding the A & P from arguments, local parameters, or globals. # !!!!!!! If you change this, remember to change round(), too! !!!!!!!!!! # This procedure finds the round parameters, but it is for speed reasons # duplicated in round. Otherwise, it is tested by the testsuite and used # by fdiv(). # returns ($self) or ($self,$a,$p,$r) - sets $self to NaN of both A and P # were requested/defined (locally or globally or both) my ($self,$a,$p,$r,@args) = @_; # $a accuracy, if given by caller # $p precision, if given by caller # $r round_mode, if given by caller # @args all 'other' arguments (0 for unary, 1 for binary ops) 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) { foreach ($self,@args) { # take the defined one, or if both defined, the one that is smaller $a = $_->{_a} if (defined $_->{_a}) && (!defined $a || $_->{_a} < $a); } } if (!defined $p) { # even if $a is defined, take $p, to signal error for both defined foreach ($self,@args) { # take the defined one, or if both defined, the one that is bigger # -2 > -3, and 3 > 2 $p = $_->{_p} if (defined $_->{_p}) && (!defined $p || $_->{_p} > $p); } } # if still none defined, use globals (#2) $a = ${"$c\::accuracy"} unless defined $a; $p = ${"$c\::precision"} unless defined $p; # A == 0 is useless, so undef it to signal no rounding $a = undef if defined $a && $a == 0; # no rounding today? return ($self) unless defined $a || defined $p; # early out # set A and set P is an fatal error return ($self->bnan()) if defined $a && defined $p; # error $r = ${"$c\::round_mode"} unless defined $r; if ($r !~ /^(even|odd|\+inf|\-inf|zero|trunc|common)$/) { require Carp; Carp::croak ("Unknown round mode '$r'"); } $a = int($a) if defined $a; $p = int($p) if defined $p; ($self,$a,$p,$r); }sub round { # Round $self according to given parameters, or given second argument's # parameters or global defaults # for speed reasons, _find_round_parameters is embeded here: my ($self,$a,$p,$r,@args) = @_; # $a accuracy, if given by caller # $p precision, if given by caller # $r round_mode, if given by caller # @args all 'other' arguments (0 for unary, 1 for binary ops) my $c = ref($self); # find out class of argument(s)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -