Skip Menu |

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

Report information
The Basics
Id: 93887
Status: open
Priority: 0/
Queue: Math-BigInt

Owner: Nobody in particular
Requestors: bitcard [...]

Bug Information
Severity: Normal
Broken in: (no value)
Fixed in: (no value)

Subject: Math::BigInt->new() wrongly converts large floats
MIME-Version: 1.0
X-Mailer: MIME-tools 5.504 (Entity 5.504)
Content-Disposition: inline
X-RT-Interface: Web
Message-ID: <rt-4.0.18-10831-1394897983-212.0-0-0 [...]>
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: binary
X-RT-Original-Encoding: utf-8
X-RT-Encrypt: 0
X-RT-Sign: 0
Content-Length: 1327
Download (untitled) / with headers
text/plain 1.2k
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].
MIME-Version: 1.0
In-Reply-To: <rt-4.0.18-10831-1394897983-212.0-0-0 [...]>
X-Mailer: MIME-tools 5.504 (Entity 5.504)
Content-Disposition: inline
X-RT-Interface: Web
References: <rt-4.0.18-10831-1394897983-212.0-0-0 [...]>
Content-Type: text/plain; charset="utf-8"
Message-ID: <rt-4.0.18-4663-1397657077-539.93887-0-0 [...]>
Content-Transfer-Encoding: binary
X-RT-Original-Encoding: utf-8
X-RT-Encrypt: 0
X-RT-Sign: 0
Content-Length: 612
Download (untitled) / with headers
text/plain 612b
Good catch! Alas, your suggested fix isn't good enough. The sprintf("%f", ...) conversion would also be applied to input like 0.00000005044031582654955529, since it's stringified version different: it's "5.04403158265496e-08". Now, sprintf("%f", 0.00000005044031582654955529) returns "0.000000", which Math::BigInt returns as zero. However, the current behaviour is to return NaN when the input is something other than an integer. This behaviour is likely to change, so that the semantics are the same as those of core Perl, but until then, we have to use a better fix. We can't introduce inconsistent behaviour.
MIME-Version: 1.0
X-Spam-Status: No, score=-4 tagged_above=-99.9 required=10 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, FROM_OUR_RT=-2] autolearn=ham
In-Reply-To: <rt-4.0.18-4663-1397657077-266.93887-6-0 [...]>
X-Spam-Flag: NO
X-RZG-Class-ID: mo00
X-RT-Interface: API
References: <RT-Ticket-93887 [...]> <rt-4.0.18-10831-1394897983-212.93887-6-0 [...]> <rt-4.0.18-4663-1397657077-266.93887-6-0 [...]>
X-Virus-Scanned: Debian amavisd-new at
Message-ID: <alpine.LNX.2.03.1404162232290.1345 [...] schizo.localdomain>
content-type: TEXT/PLAIN; charset="utf-8"; format="flowed"
X-X-Sender: vs [...] schizo.localdomain
X-RT-Original-Encoding: utf-8
X-Spam-Score: -4
Authentication-Results: (amavisd-new); dkim=pass header.i= [...]
Received: from localhost (localhost []) by (Postfix) with ESMTP id 5ABC0240651 for <cpan-bug+Math-BigInt [...]>; Wed, 16 Apr 2014 16:59:22 -0400 (EDT)
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id Ib6O7aVTLHCh for <cpan-bug+Math-BigInt [...]>; Wed, 16 Apr 2014 16:59:21 -0400 (EDT)
Received: from ( []) by (Postfix) with SMTP id E2D402403B1 for <bug-Math-BigInt [...]>; Wed, 16 Apr 2014 16:59:20 -0400 (EDT)
Received: (qmail 23390 invoked by alias); 16 Apr 2014 20:59:20 -0000
Received: from (HELO ( by (qpsmtpd/0.28) with ESMTP; Wed, 16 Apr 2014 13:59:17 -0700
Received: from schizo ( []) by (RZmta 32.35 DYNA|AUTH) with ESMTPSA id U06cffq3GKxBGDg (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) for <bug-Math-BigInt [...]>; Wed, 16 Apr 2014 22:59:11 +0200 (CEST)
Delivered-To: cpan-bug+Math-BigInt [...]
Subject: Re: [ #93887] Math::BigInt->new() wrongly converts large floats
User-Agent: Alpine 2.03 (LNX 1266 2009-07-14)
Return-Path: <foss [...]>
Dkim-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; t=1397681952; l=1951; s=domk;; h=Content-Type:MIME-Version:References:In-Reply-To:Subject:To:From: Date:X-RZG-CLASS-ID:X-RZG-AUTH; bh=B8XzSW0xWz6K7uCrJ+GompH1qyU=; b=UWMg0NoDQEqh8H3k53g6GAN2WPUvBsaWUN5SgDEmJ6ZtckILXJo68+0RRCHnWq12SFO Vem664w9INYmr+h81pfG6MorYUHhKxVdoUX91ElFd0WFlvJSug5w6FpIf0zgxPBYEAVKP qq2NRkPlsl/bRV6Cw4nqHLTHO/mYGy4CvkU=
X-Original-To: cpan-bug+Math-BigInt [...]
X-RT-Mail-Extension: math-bigint
Date: Wed, 16 Apr 2014 22:59:06 +0200 (CEST)
X-RZG-Auth: :P2EQZWC+dPMoc+4qF3SJXKZyr+5pInq/I5DtJ1a9J8UuBQwsOjf8OhiSU+F5Lwxi
To: Peter John Acklam via RT <bug-Math-BigInt [...]>
From: Volker Schatz <foss [...]>
RT-Message-ID: <rt-4.0.18-701-1397681963-14.93887-0-0 [...]>
Content-Length: 1979
Download (untitled) / with headers
text/plain 1.9k
Thanks for reminding me of fractional input, I had not thought of that. Playing around with non-integers, I found two more test cases that are broken in 1.9991: 4503599627370495.5 = 2**52 - 0.5, the largest non-integer representable as a double. This needs to be sprintf'ed because autoconversion discards some digits, including the fractional digit that triggers the NaN. This and all values <1 and >0 are fixed by replacing the line inserted in my patch by: $x= sprintf("%f", $x) if "$x" != $x && $x > 1; The second, more problematic test case I dug up is 1+DBL_EPSILON = 1.00000000000000022. This is handled correctly in _split() if one raises the precision of the sprintf: $x= sprintf("%.17f", $x) if "$x" != $x && $x > 1; 17 = DBL_DECIMAL_DIG, the number of digits sufficient for a double->string->double roundtrip conversion. However, in this case split() is never reached because of a "shortcut" in new() that also applies a regex to the autoconverted value. Applying the same roundtrip conversion check as in _split() should fix this too: @@ -534,7 +534,7 @@ my $self = bless {}, $class; # shortcut for "normal" numbers - if ((!ref $wanted) && ($wanted =~ /^([+-]?)[1-9][0-9]*\z/)) + if ((!ref $wanted) && "$wanted" == $wanted && ($wanted =~ /^([+-]?)[1-9][0-9]*\z/)) { $self->{sign} = $1 || '+'; @@ -3048,6 +3048,8 @@ # invalid input. my $x = shift; + $x= sprintf("%.17f", $x) if "$x" != $x && $x > 1; # 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 This is quite an effort to make for the NaN behaviour, considering it is on the way out, and has never worked for large numbers like 2**52 - 0.5. I hope you remember to revert back to the version from my last post when the time comes, so code complexity is kept down a little.

This service is sponsored and maintained by Best Practical Solutions and runs on infrastructure.

Please report any issues with to