Skip Menu |

This queue is for tickets about the Math-BigInt CPAN distribution.

Report information
The Basics
Id: 6253
Status: resolved
Worked: 2 min
Priority: 0/
Queue: Math-BigInt

People
Owner: TELS [...] cpan.org
Requestors: jeff [...] thekidders.com
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in:
  • 1.60
  • 1.61
  • 1.62
  • 1.63
  • 1.64
  • 1.65
  • 1.66
  • 1.67
  • 1.68
  • 1.69
  • 1.70
  • 1.71
  • 1.72
Fixed in: 1.73



Subject: bpow doesn't handle negative first arguments correctly
This file, BigIntTest.pl: use Math::BigInt; use PowFix; sub ppow { my $x = shift; my $y = shift; my $expect = shift; my $res = Math::BigInt->bpow($x,$y); my $myres = Math::BigInt->kpow($x,$y); print "$x**$y = $res"; if ($res eq $expect) { print " \t#as expected.\n"; } else { print " \t#should this be $expect?\n"; } if ($myres eq $expect) { # print " \t#as expected.\n"; } else { print "\t#oops, I get $x**$y=$myres not $expect?\n"; } } &ppow(-2,2,4); &ppow('-inf',2,'inf'); &ppow('-inf',0,'NaN'); &ppow('-inf',-1,0); &ppow('-inf','inf','NaN'); &ppow(2,'inf','inf'); &ppow(2,'-inf',0); &ppow(0,'inf',0); &ppow(0,'-inf','inf'); &ppow(-2,'inf','NaN'); &ppow(-2,'-inf',0); &ppow(-1,'-inf','NaN'); &ppow(-1,'inf','NaN'); &ppow('-inf','NaN','NaN'); &ppow('inf','NaN','NaN'); &ppow(1,'inf',1); &ppow(1,'-inf',1); &ppow(0,0,1); &ppow(-1,-1,-1); &ppow(-2,3,-8); Produces this output: Show quoted text
> perl -w BigIntTest.pl
-2**2 = -4 #should this be 4? -inf**2 = -inf #should this be inf? -inf**0 = -inf #should this be NaN? -inf**-1 = -inf #should this be 0? -inf**inf = -inf #should this be NaN? 2**inf = 2 #should this be inf? 2**-inf = 2 #should this be 0? 0**inf = 0 #as expected. 0**-inf = 0 #should this be inf? -2**inf = -2 #should this be NaN? -2**-inf = -2 #should this be 0? -1**-inf = 1 #should this be NaN? -1**inf = 1 #should this be NaN? -inf**NaN = -inf #should this be NaN? inf**NaN = inf #should this be NaN? 1**inf = 1 #as expected. 1**-inf = 1 #as expected. 0**0 = 1 #as expected. -1**-1 = -1 #as expected. -2**3 = -8 #as expected. With my proposed fix in the module PowFix.pm: package Math::BigInt; my $nan = "NaN"; my $CALC = 'Math::BigInt::Calc'; # module to do low level math sub kpow { # (BINT or num_str, BINT or num_str) return BINT # compute power of two numbers -- stolen from Knuth Vol 2 pg 233 # modifies first argument # 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('bpow'); return $upgrade->bpow($upgrade->new($x),$y,@r) if defined $upgrade && !$y->isa($self); $r[3] = $y; # no push! #if x or y is NaN x**y = NaN return $x->bnan() if ($x->{sign} eq $nan || $y->{sign} eq $nan); #Now that NaNs are gone.. # if x==0, 0**y = inf/1/0 if y is <0/==0/>0 if($x->{sign} eq '+' && $CALC->_is_zero($x->{value})) { # e.g., 0 ** -7 => ( 1 / (0 ** 7)) => 1 / 0 => +inf return $x->binf() if $y->{sign} =~ /^-/; # -inf or y<0 # y=0: 0**0 = 1 return $x->bone() if $y->{sign} eq '+' && $CALC->_is_zero($y->{value}); # y>0 is only case left # y==+inf or y>0: 0**y = 0 return $x; } if ($CALC->_is_one($x->{value})) { #if x==1, 1**y = 1, even for y = +/-inf return $x if $x->{sign} eq '+'; # if x == -1 ... # -1 ** +/-inf = NaN since inf is neither odd nor even return $x->bnan() if $y->{sign} =~ /^[-+]inf/; # x<0: x ** +inf # -1 ** y = -1 if y is odd and 1 if y is even my $new_sign = $y->is_odd() ? '-' : '+'; $x->{sign} = $new_sign; # -inf/+inf ** y odd/even return $x; } # if y == inf if ($y->{sign} eq '+inf') { # x ** +inf (x is not NaN, +/-Inf) # x<0: x ** +inf = NaN return $x->bnan() if $x->{sign} =~ /^-/; # we have ruled out x is -inf, x<0, 0, 1, or NaN, so inf>x>1... # x ** inf = inf for x > 1 return $x->binf(); # x>1: x ** +inf } # if y == -inf if ($y->{sign} eq '-inf') { # we have ruled out x is 0, -1, 1, or NaN, so |x|>1... # x ** -inf = 0 for |x| > 1 return $x->bzero(); } # if x == +/-inf if ($x->{sign} =~ /^[+-]inf$/) { # +/-inf ** 0 = NaN return $x->bnan() if $CALC->_is_zero($y->{value}); # y<0: +/-inf ** y = 0 return $x->bzero() if $y->{sign} =~ /^-/; # we have ruled out y is 0, <0, -Inf, NaN, or +inf, so y>0... # inf>y>0: inf ** y = inf # inf>y>0: -inf ** y = -inf if y is odd and 1 if y is even if ($x->{sign} eq '-inf') { my $new_sign = $y->is_odd() ? '-inf' : '+inf'; $x->{sign} = $new_sign; # -inf/+inf ** y odd/even } return $x; } # cases 0 ** Y, X ** 0, X ** 1, 1 ** Y are handled by Calc or Emu # but we have handled 0 ** Y, 1 ** Y, and -1 ** Y already my $new_sign = '+'; $new_sign = $y->is_odd() ? '-' : '+' if ($x->{sign} ne '+'); # |x|>1, y<0: x ** y => 1 / (x ** -y) # so do test for negative $y after above's clause return $x->bnan() if $y->{sign} eq '-'; #if rounding is allowed, shouldn't we round? $x->{value} = $CALC->_pow($x->{value},$y->{value}); $x->{sign} = $new_sign; $x->{sign} = '+' if $CALC->_is_zero($y->{value}); $x->round(@r) if !exists $x->{_f} || $x->{_f} & MB_NEVER_ROUND == 0; $x; } 1; Show quoted text
> perl -v
This is perl, v5.8.0 built for MSWin32-x86-multi-thread (with 1 registered patch, see perl -V for more detail) Copyright 1987-2002, Larry Wall Binary build 805 provided by ActiveState Corp. http://www.ActiveState.com Built 18:08:02 Feb 4 2003 I have also tried this on 5.00503. As well as under linux with perl 5.8.2. Regards, Jeff
From: perl_dummy [...] bloodgate.com
[guest - Thu May 6 20:57:59 2004]: Helo Jeff, thank you for your detailed report and fix! I totally miss the rt bugs because somehow their email never reaches me :( Sorry for the late reply. I got a similiar bug report about negative powers today, so I am looking now for 1.73 to fix these bugs. Best wishes, Tels