diff options
author | Peter John Acklam <pjacklam@online.no> | 2010-12-26 13:17:16 -0800 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2010-12-26 14:37:05 -0800 |
commit | e5dcd524f24400d4c59ff25e99f07d5397388205 (patch) | |
tree | 21f703ef87cb3ed690f591dd045c40d4608c6ed1 /dist | |
parent | f5d415344cd77e5641abbd31dc8d3080776508ea (diff) | |
download | perl-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')
-rw-r--r-- | dist/Math-BigInt/lib/Math/BigInt/Calc.pm | 23 |
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; } ############################################################################## |