Perl does not distinguish between integer and floating-point numbers when doing arithmetic. Machine Arithmetic where the operands and the result are all integral is accurate as long as all values can be properly represented by the platform. Unfortunately, floating-point arithmetic is inherently imprecise, and can trip programmers who are not aware of its usage.
Noncompliant Code Example
This noncompliant code example appears to print ten very large numbers, and on 64-bit machines, it indeed does so.
However, when run on a 32-bit machine, the loop will never terminate. This is because the numbers, while integral, are too large to be represented internally as integers, so they are represented as 32-bit floating-point numbers. Even with 24-bit mantissas, the numbers are too large for an increment of 1 to be noticed. Consequently, the program forever prints out the number 1e+16
.
my $x = 10000000000000000; # 1e+16 for (my $y = $x; $y <= $x + 5; $y += 1) { print "$y\n"; }
Compliant Solution
This compliant solution ensures that the loop counter computation involves numbers less than 2^{48 }(that is, 281,474,976,710,656).
my $x = 10000000000000000; # 1e+16 for (my $y = 0; $y <= 5; $y += 1) { my $z = $x + $y; print "$z\n"; }
On a 32-bit machine, this program terminates normally after printing the following:
1e+16
1e+16
1e+16
1e+16
1e+16
1e+16
Compliant Solution
This compliant solution uses the Bignum module to ensure precise computation. The Bignum module is available in CPAN, but became part of Perl's standard library for version 5.8.
use bignum; my $x = 10000000000000000; # 1e+16 for (my $y = $x; $y <= $x + 5; $y += 1) { print "$y\n"; }
On a 32-bit machine, this program terminates normally after printing the following:
10000000000000000
10000000000000001
10000000000000002
10000000000000003
10000000000000004
10000000000000005
Risk Assessment
Failing to understand the limitations of floating-point numbers can result in unexpected computational results and exceptional conditions, possibly resulting in a violation of data integrity.
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
INT01-PL | medium | probable | high | P4 | L3 |
Bibliography
[Gough 2005] | Section 8.6, "Floating-point issues" |
[IEEE 754 2006] | |
[CPAN] | Florian Ragwitz, bignum |
[Meta CPAN] | perlnumber |
4 Comments
thiago glauco sanchez
Just a note about the second compliant solution:
The Bignum has been added to Perl Standard Library in Perl 5.14 and on. Now it is a pragmatic module.
Regards.
David Svoboda
Added a note to this info...thanks!
Anonymous
Actually
bignum
was added to core in 5.8.0 not 5.14.0use
corelist
next time to check for when a module was added to core.Unrelated:
Why is bignum using a version specific search.cpan.org link and perlnumber using a metacpan link?
perlnumber should probably be perldoc.perl.org link http://perldoc.perl.org/perlnumber.html
(http://p3rl.org/perlnumber will redirect to the same place)
bignum should be one of the following ( ordered from most preferred to least preferred. )
I would also recommend against specifying a person as it only matters who was allowed to upload the module to PAUSE at the time it was uploaded.
( You could use p3rl.org links )
David Svoboda
Thanks for the versioning info and the links. I updated the rule with them.