📄 bigint.pm
字号:
# 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) || $y->{sign} eq '-'); $r[3] = $y; # no push! # cases 0 ** Y, X ** 0, X ** 1, 1 ** Y are handled by Calc or Emu 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 '-' && !$CALC->_is_one($x->{value}); $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 { # (BINT or num_str, BINT or num_str) return BINT # compute x << y, base n, y >= 0 # set up parameters my ($self,$x,$y,$n,@r) = (ref($_[0]),@_); # objectify is costly, so avoid it if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) { ($self,$x,$y,$n,@r) = objectify(2,@_); } return $x if $x->modify('blsft'); return $x->bnan() if ($x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/); return $x->round(@r) if $y->is_zero(); $n = 2 if !defined $n; return $x->bnan() if $n <= 0 || $y->{sign} eq '-'; $x->{value} = $CALC->_lsft($x->{value},$y->{value},$n); $x->round(@r); }sub brsft { # (BINT or num_str, BINT or num_str) return BINT # compute x >> y, base n, y >= 0 # set up parameters my ($self,$x,$y,$n,@r) = (ref($_[0]),@_); # objectify is costly, so avoid it if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) { ($self,$x,$y,$n,@r) = objectify(2,@_); } return $x if $x->modify('brsft'); return $x->bnan() if ($x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/); return $x->round(@r) if $y->is_zero(); return $x->bzero(@r) if $x->is_zero(); # 0 => 0 $n = 2 if !defined $n; return $x->bnan() if $n <= 0 || $y->{sign} eq '-'; # this only works for negative numbers when shifting in base 2 if (($x->{sign} eq '-') && ($n == 2)) { return $x->round(@r) if $x->is_one('-'); # -1 => -1 if (!$y->is_one()) { # although this is O(N*N) in calc (as_bin!) it is O(N) in Pari et al # but perhaps there is a better emulation for two's complement shift... # if $y != 1, we must simulate it by doing: # convert to bin, flip all bits, shift, and be done $x->binc(); # -3 => -2 my $bin = $x->as_bin(); $bin =~ s/^-0b//; # strip '-0b' prefix $bin =~ tr/10/01/; # flip bits # now shift if ($y >= CORE::length($bin)) { $bin = '0'; # shifting to far right creates -1 # 0, because later increment makes # that 1, attached '-' makes it '-1' # because -1 >> x == -1 ! } else { $bin =~ s/.{$y}$//; # cut off at the right side $bin = '1' . $bin; # extend left side by one dummy '1' $bin =~ tr/10/01/; # flip bits back } my $res = $self->new('0b'.$bin); # add prefix and convert back $res->binc(); # remember to increment $x->{value} = $res->{value}; # take over value return $x->round(@r); # we are done now, magic, isn't? } # x < 0, n == 2, y == 1 $x->bdec(); # n == 2, but $y == 1: this fixes it } $x->{value} = $CALC->_rsft($x->{value},$y->{value},$n); $x->round(@r); }sub band { #(BINT or num_str, BINT or num_str) return BINT # compute x & y # 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,@_); } return $x if $x->modify('band'); $r[3] = $y; # no push! return $x->bnan() if ($x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/); my $sx = $x->{sign} eq '+' ? 1 : -1; my $sy = $y->{sign} eq '+' ? 1 : -1; if ($sx == 1 && $sy == 1) { $x->{value} = $CALC->_and($x->{value},$y->{value}); return $x->round(@r); } if ($CAN{signed_and}) { $x->{value} = $CALC->_signed_and($x->{value},$y->{value},$sx,$sy); return $x->round(@r); } require $EMU_LIB; __emu_band($self,$x,$y,$sx,$sy,@r); }sub bior { #(BINT or num_str, BINT or num_str) return BINT # compute x | y # 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,@_); } return $x if $x->modify('bior'); $r[3] = $y; # no push! return $x->bnan() if ($x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/); my $sx = $x->{sign} eq '+' ? 1 : -1; my $sy = $y->{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 ($sx == 1 && $sy == 1) { $x->{value} = $CALC->_or($x->{value},$y->{value}); return $x->round(@r); } # if lib can do negative values, let it handle this if ($CAN{signed_or}) { $x->{value} = $CALC->_signed_or($x->{value},$y->{value},$sx,$sy); return $x->round(@r); } require $EMU_LIB; __emu_bior($self,$x,$y,$sx,$sy,@r); }sub bxor { #(BINT or num_str, BINT or num_str) return BINT # compute x ^ y # 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,@_); } return $x if $x->modify('bxor'); $r[3] = $y; # no push! return $x->bnan() if ($x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/); my $sx = $x->{sign} eq '+' ? 1 : -1; my $sy = $y->{sign} eq '+' ? 1 : -1; # don't use lib for negative values if ($sx == 1 && $sy == 1) { $x->{value} = $CALC->_xor($x->{value},$y->{value}); return $x->round(@r); } # if lib can do negative values, let it handle this if ($CAN{signed_xor}) { $x->{value} = $CALC->_signed_xor($x->{value},$y->{value},$sx,$sy); return $x->round(@r); } require $EMU_LIB; __emu_bxor($self,$x,$y,$sx,$sy,@r); }sub length { my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); my $e = $CALC->_len($x->{value}); wantarray ? ($e,0) : $e; }sub digit { # return the nth decimal digit, negative values count backward, 0 is right my ($self,$x,$n) = ref($_[0]) ? (undef,@_) : objectify(1,@_); $n = $n->numify() if ref($n); $CALC->_digit($x->{value},$n||0); }sub _trailing_zeros { # return the amount of trailing zeros in $x (as scalar) my $x = shift; $x = $class->new($x) unless ref $x; return 0 if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf etc $CALC->_zeros($x->{value}); # must handle odd values, 0 etc }sub bsqrt { # calculate square root of $x my ($self,$x,@r) = ref($_[0]) ? (undef,@_) : objectify(1,@_); return $x if $x->modify('bsqrt'); return $x->bnan() if $x->{sign} !~ /^\+/; # -x or -inf or NaN => NaN return $x if $x->{sign} eq '+inf'; # sqrt(+inf) == inf return $upgrade->bsqrt($x,@r) if defined $upgrade; $x->{value} = $CALC->_sqrt($x->{value}); $x->round(@r); }sub broot { # calculate $y'th root of $x # set up parameters my ($self,$x,$y,@r) = (ref($_[0]),@_); $y = $self->new(2) unless defined $y; # objectify is costly, so avoid it if ((!ref($x)) || (ref($x) ne ref($y))) { ($self,$x,$y,@r) = objectify(2,$self || $class,@_); } return $x if $x->modify('broot'); # NaN handling: $x ** 1/0, x or y NaN, or y inf/-inf or y == 0 return $x->bnan() if $x->{sign} !~ /^\+/ || $y->is_zero() || $y->{sign} !~ /^\+$/; return $x->round(@r) if $x->is_zero() || $x->is_one() || $x->is_inf() || $y->is_one(); return $upgrade->new($x)->broot($upgrade->new($y),@r) if defined $upgrade; $x->{value} = $CALC->_root($x->{value},$y->{value}); $x->round(@r); }sub exponent { # return a copy of the exponent (here always 0, NaN or 1 for $m == 0) my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); if ($x->{sign} !~ /^[+-]$/) { my $s = $x->{sign}; $s =~ s/^[+-]//; # NaN, -inf,+inf => NaN or inf return $self->new($s); } return $self->bone() if $x->is_zero(); # 12300 => 2 trailing zeros => exponent is 2 $self->new( $CALC->_zeros($x->{value}) ); }sub mantissa { # return the mantissa (compatible to Math::BigFloat, e.g. reduced) my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); if ($x->{sign} !~ /^[+-]$/) { # for NaN, +inf, -inf: keep the sign return $self->new($x->{sign}); } my $m = $x->copy(); delete $m->{_p}; delete $m->{_a}; # that's a bit inefficient: my $zeros = $CALC->_zeros($m->{value}); $m->brsft($zeros,10) if $zeros != 0; $m; }sub parts { # return a copy of both the exponent and the mantissa my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); ($x->mantissa(),$x->exponent()); } ############################################################################### rounding functionssub bfround { # precision: round to the $Nth digit left (+$n) or right (-$n) from the '.' # $n == 0 || $n == 1 => round to integer my $x = shift; my $self = ref($x) || $x; $x = $self->new($x) unless ref $x; 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; delete $x->{_a}; # delete to save memory $x->{_p} = $scale; # store new _p $x; }sub _scan_for_nonzero { # internal, used by bround() to scan for non-zeros after a '5' my ($x,$pad,$xs,$len) = @_; return 0 if $len == 1; # "5" is trailed by invisible zeros my $follow = $pad - 1; return 0 if $follow > $len || $follow < 1; # 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; $x = $class->new($x) unless ref $x; $x->bround(@_); }sub bround { # accuracy: +$n preserve $n digits from left, # -$n preserve $n digits from right (f.i. for 0.1234 style in MBF) # no-op for $n == 0 # and overwrite the rest with 0's, return normalized number # do not return $x->bnorm(), but $x my $x = shift; $x = $class->new($x) unless ref $x; my ($scale,$mode) = $x->_scale_a(@_); return $x if !defined $scale || $x->modify('bround'); # no-op if ($x->is_zero() || $scale == 0) { $x->{_a} = $scale if !defined $x->{_a} || $x->{_a} > $scale; # 3 > 2 return $x; } return $x if $x->{sign} !~ /^[+-]$/; # inf, NaN # we have fewer digits than we want to scale to my $len = $x->length(); # convert $scale to a scalar in case it is an object (put's a limit on the # number length, but this would already limited by memory constraints), makes # it faster $scale = $scale->numify() if ref ($scale); # scale < 0, but > -len (not >=!) if (($scale < 0 && $scale < -$len-1) || ($scale >= $len)) { $x->{_a} = $scale if !defined $x->{_a} || $x->{_a} > $scale; # 3 > 2 return $x; } # count of 0's to pad, from left (+) or right (-): 9 - +6 => 3, or |-6| => 6 my
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -