|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2011-02-20 14:17 UTC] mr_platelet+jin6vr at fastmail dot fm
Description:
------------
In the script below, number_format returns a string
representing zero, which is quite wrong.
Test script:
---------------
<?php
$x = pow(2, -1074);
$s = number_format($x, 1074, '.', '');
if(trim($s, '0') === '.') {
print "\$s represents zero, which is wrong.\n";
}
Expected result:
----------------
I expect to see no output.
Actual result:
--------------
The script outputs the following message:
$s represents zero, which is wrong.
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sat Nov 29 01:00:01 2025 UTC |
The following script shows further problems with number_format. ------------------ <?php $count = 0; $wrong = 0; for($i = 319; $i <= 1074; $i++) { $s1 = number_format(pow(2, -$i), $i, '.', ''); $s2 = bcpow(2, -$i, $i); $s1 = trim($s1, '0'); $s2 = trim($s2, '0'); $count++; $wrong += ($s1 !== $s2); } print "Number of calculations: $count\n"; print "Number of wrong answers: $wrong\n"; ------------------ When I run the script, it prints: Number of calculations: 756 Number of wrong answers: 756 Some extra information. (1) When running each of the 2 scripts I've supplied, I used PHP's "-n" switch. (2) Here is the "configure" command I used when building PHP: --prefix=/usr/local/php/5.3.5 --disable-all --enable-bcmathArbitrary precision is not needed, because in each of the two scripts, the numbers generated using pow can be perfectly represented as IEEE double-precision floating-point numbers. I'll add an extra line of code to the first script in order to explain better what the problem is. Here's the new script. ------------------- <?php $x = pow(2, -1074); # new line of code here: printf("This shows that \$x is not zero:\n %.53e\n\n", $x); $s = number_format($x, 1074, '.', ''); if(trim($s, '0') === '.') { print "\$s represents zero, which is wrong.\n"; } ------------------- On my system, the output of the script is: ------------------- This shows that $x is not zero: 4.94065645841246544176568792868221372365059802614324764e-324 $s represents zero, which is wrong. ------------------- $s definitely should not represent zero, because 1074 (the second argument of number_format) is exactly the number of decimal digits needed to perfectly represent 2 to the power of -1074. More generally, for any positive integer n, the number 2^-n can be perfectly represented in base 10 using n decimal digits, as can be seen by considering the following sequence: 2^-1 = 0.5 2^-2 = 0.25 2^-3 = 0.125 2^-4 = 0.0625 2^-5 = 0.03125 2^-6 = 0.015625 2^-7 = 0.0078125 2^-8 = 0.00390625 2^-9 = 0.001953125