Subject: | Math::BigInt->new() wrongly converts large floats |
A conversion of large integral floating-point numbers into BigInts can lose
significant digits. Test case: 504403158265495552.0 == 7 * 2**56, which is
exactly representable as a float/double.
$ perl -MMath::BigInt -e 'print Math::BigInt->new(504403158265495552.0)->bstr(), "\n";'
504403158265496000
A test case that reqires more than 64 integer bits is 7 * 2**66 = 516508834063867445248.0.
The cause is that the _split function in Math::BigInt treats its argument as a string and hereby triggers Perl's automatic conversion which does not output enough digits for large integers. A fix is to explicitly convert via sprintf("%f",..), which retains all digits before the decimal point. Doing this for non-floats is superfluous and possibly harmful, so we test the accuracy of a roundtrip with the automatic conversion first:
@@ -3048,6 +3048,8 @@
# invalid input.
my $x = shift;
+ $x= sprintf("%f", $x) unless "$x" == $x; # ensure enough digits from float
+
# strip white space at front, also extraneous leading zeros
$x =~ s/^\s*([-]?)0*([0-9])/$1$2/g; # will not strip ' .2'
$x =~ s/^\s+//; # but this will
Line numbers refer to version 1.9991, which my distribution ships but CPAN does not yet (?!).
This issue was first reported to the perlbug list, as [perl #121136].