php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #54056 number_format returns an incorrect answer for certain parameters
Submitted: 2011-02-20 14:17 UTC Modified: 2011-02-21 08:17 UTC
From: mr_platelet+jin6vr at fastmail dot fm Assigned:
Status: Wont fix Package: Strings related
PHP Version: 5.3.5 OS: Linux
Private report: No CVE-ID: None
 [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.


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-02-20 14:33 UTC] mr_platelet+jin6vr at fastmail dot fm
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-bcmath
 [2011-02-20 16:37 UTC] rasmus@php.net
-Status: Open +Status: Feedback
 [2011-02-20 16:37 UTC] rasmus@php.net
I'm not sure what you are expecting here. number_format() is not an arbitrary 
precision function. You are limited by the double precision of your operating 
system.
 [2011-02-20 17:19 UTC] mr_platelet+jin6vr at fastmail dot fm
-Status: Feedback +Status: Open
 [2011-02-20 17:19 UTC] mr_platelet+jin6vr at fastmail dot fm
Arbitrary 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
 [2011-02-21 08:17 UTC] cataphract@php.net
-Status: Open +Status: Wont fix
 [2011-02-21 08:17 UTC] cataphract@php.net
PHP's implementation of spprintf, used by number_format, admits (if I'm reading the code right) a maximum of 318 digits for the 'f' mode and 319 for 'e'.

This is because PHP relies on a buffer with 500 bytes (formatted_print.c) or 512 bytes (snprint.c and spprintf.c) to hold the resulting conversion of the number, so a larger number of digits could overflow the buffer.

Given this, we could increase the buffer, limit the number of digits, or dynamically allocate the buffer. Given the near absence of interest in such number of decimal places, the current solution of limiting the number of digits seems appropriate.

Won't fix.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 07 09:01:28 2024 UTC