summaryrefslogtreecommitdiff
path: root/dist/Math-BigInt
diff options
context:
space:
mode:
authorPeter John Acklam <pjacklam@online.no>2010-12-26 13:17:16 -0800
committerFather Chrysostomos <sprout@cpan.org>2010-12-26 14:37:05 -0800
commite5dcd524f24400d4c59ff25e99f07d5397388205 (patch)
tree21f703ef87cb3ed690f591dd045c40d4608c6ed1 /dist/Math-BigInt
parentf5d415344cd77e5641abbd31dc8d3080776508ea (diff)
downloadperl-e5dcd524f24400d4c59ff25e99f07d5397388205.tar.gz
Fix RT 25274: Math::BigInt::Calc->_num() overflow.
Why: Math::BigInt::Calc->_num() converts a big integer (in the internal format) to a Perl scalar. If the big integer is too large to be represented as a Perl scalar, it might return a Perl scalar numeric "nan", rather than "inf". The reason is that the current algorithm might multiply "inf" by "0", giving a "nan" which propagates. The following example illustrates the bug: perl -MMath::BigInt=lib,Calc -wle \ 'print Math::BigInt->new("1e999999")->numify()' How: This fix computes the output in a different way, never multiply "inf" by "0". Test: It is not obvious to me how to test this automatically in a portable way, since Perl has no standard way of stringifying a scalar numeric infinity. However the desired behaviour is verified manually and no existing tests fail with the new code.
Diffstat (limited to 'dist/Math-BigInt')
-rw-r--r--dist/Math-BigInt/lib/Math/BigInt/Calc.pm23
1 files changed, 14 insertions, 9 deletions
diff --git a/dist/Math-BigInt/lib/Math/BigInt/Calc.pm b/dist/Math-BigInt/lib/Math/BigInt/Calc.pm
index 3cb202501c..c778dcdf45 100644
--- a/dist/Math-BigInt/lib/Math/BigInt/Calc.pm
+++ b/dist/Math-BigInt/lib/Math/BigInt/Calc.pm
@@ -272,17 +272,22 @@ sub _str
sub _num
{
- # Make a number (scalar int/float) from a BigInt object
- my $x = $_[1];
+ # Make a Perl scalar number (int/float) from a BigInt object.
+ my $x = $_[1];
- return 0+$x->[0] if scalar @$x == 1; # below $BASE
- my $fac = 1;
- my $num = 0;
- foreach (@$x)
- {
- $num += $fac*$_; $fac *= $BASE;
+ return 0 + $x->[0] if scalar @$x == 1; # below $BASE
+
+ # Start with the most significant element and work towards the least
+ # significant element. Avoid multiplying "inf" (which happens if the number
+ # overflows) with "0" (if there are zero elements in $x) since this gives
+ # "nan" which propagates to the output.
+
+ my $num = 0;
+ for (my $i = $#$x ; $i >= 0 ; --$i) {
+ $num *= $BASE;
+ $num += $x -> [$i];
}
- $num;
+ return $num;
}
##############################################################################