develooper Front page | perl.perl5.porters | Postings from December 2010

[perl #81170] [PATCH] Fix RT 25274: Math::BigInt::Calc->_num() overflow.

From:
perlbug-followup
Date:
December 22, 2010 18:37
Subject:
[perl #81170] [PATCH] Fix RT 25274: Math::BigInt::Calc->_num() overflow.
Message ID:
rt-3.6.HEAD-5425-1293043391-455.81170-75-0@perl.org
# New Ticket Created by  (Peter J. Acklam) 
# Please include the string:  [perl #81170]
# in the subject line of all future correspondence about this issue. 
# <URL: http://rt.perl.org/rt3/Ticket/Display.html?id=81170 >


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.
---
 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 3cb2025..c778dcd 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;
   }
 
 ##############################################################################
-- 
1.7.2.3




nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About