php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #47168 printf of floating point variable prints maximum of 40 decimal places
Submitted: 2009-01-20 14:55 UTC Modified: 2010-11-19 17:36 UTC
Votes:3
Avg. Score:4.0 ± 1.4
Reproduced:1 of 2 (50.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: exploringbinary at gmail dot com Assigned: iliaa (profile)
Status: Closed Package: Math related
PHP Version: 5.2.9 OS: *
Private report: No CVE-ID: None
 [2009-01-20 14:55 UTC] exploringbinary at gmail dot com
Description:
------------
printf will not print more than 40 decimal places, even if the format specifier asks for more. For example, 

<?php
 $value = 0.00000000000000011102230246251565404236316680908203125; // 2^-53
 printf ("%1.53f",$value); // Prints only 40 decimal places: 0.0000000000000001110223024625156540423632
 ?> 

Contrast this with javascript:
<script type="text/javascript">
  var value = 0.00000000000000011102230246251565404236316680908203125; // 2^-53
  var result = value.toFixed(53);
  document.write (result); // Prints all 53 decimal places
</script>

I reported a similar bug in Visual Studio, which Microsoft acknowledged as an error: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=329278


Reproduce code:
---------------
<?php
 $value = 0.00000000000000011102230246251565404236316680908203125; // 2^-53
 printf ("%1.53f",$value); // Prints only 40 decimal places: 0.0000000000000001110223024625156540423632
 ?>

Expected result:
----------------
I expect to see all 53 decimal places printed: 0.00000000000000011102230246251565404236316680908203125

Actual result:
--------------
Only 40 decimal places are printed: 0.0000000000000001110223024625156540423632

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-01-20 18:04 UTC] iliaa@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

PHP string formatting functions will not permit float precision beyond 
40 characters, this is a hard limit. A notice was however added to 
indicate that this is happening.
 [2009-01-20 22:26 UTC] exploringbinary at gmail dot com
Hi,

  I read the documentation and the "how to submit a bug report" and googled as well before (and now again after) submitting the bug report.  I cannot see discussion of this limit. Could you please give me a more specific link?

  I would also like to know the rationale for the limit. Why doesn't javascript or gcc have this limit? Why did Microsoft accept my bug report for a similar problem in VS? It seems like something PHP should consider, or at least give rationale for why not.

Thanks.
 [2009-01-30 21:51 UTC] exploringbinary at gmail dot com
Since I didn't find anything in the documentation, I poked around in the source code. In php-5.2.8\ext\standard\formatted_print.c I found this: #define MAX_FLOAT_PRECISION 40

There is no explanation as to why it's capped at 40.

What's interesting is that this code prints only 40 decimal places (0.0000000000004547473508864641189575195312), without a warning:

<?php
 /* Print 2^-41 */
 $dyadic = 0.00000000000045474735088646411895751953125;
 printf ("%1.2147483646f",$dyadic);
?>

While this code prints ``Warning: printf() [function.printf]: Precision must be greater than zero and less than 2147483647 ...'' 

<?php
 /* Print 2^-41 */
 $dyadic = 0.00000000000045474735088646411895751953125;
 printf ("%1.2147483647f",$dyadic);
?>

If you're going to warn about a maximum, why not warn about the real maximum -- 40, not 2147483646?

I wrote an article about how this issue is handled in PHP and 7 other languages at http://www.exploringbinary.com/print-precision-of-dyadic-fractions-varies-by-language/
 [2009-04-29 05:21 UTC] rasmus@php.net
I agree with Rick here.  The 40-digit limit is strangely arbitrary and doesn't match the size of the double.  We are hiding available precision here.
 [2009-04-30 01:23 UTC] ivan dot rey at inpltda dot com
I agree also.
Full precision should be available for the developer.

I wonder if recompiling php with a different CAP in  In php-5.2.8\ext\standard\formatted_print.c I found this:
#define MAX_FLOAT_PRECISION 40 is the actual solution or if it has some further implications.
 [2009-04-30 03:48 UTC] rasmus@php.net
There are a couple of other places that need to be changed.  I have a patch but I haven't had a chance to go through and fix the test cases yet.  I'll get to it before RC2 next week.
 [2009-04-30 14:44 UTC] exploringbinary at gmail dot com
A reader of my blog pointed out that serialize and unserialize have similar issues.  I ran some tests and discovered that 2^-143 is the smallest negative power of two that PHP can serialize, and 2^-40 is the smallest it can unserialize.

Here's are the testcases:


This code works ? it prints

d:8.9683101716788292539118693330554632401936764280097009392452370
16894662929189507849514484405517578125E-44;

<?php
 //Serialize 2^-143
 $dyadic = //Compute as (2^-50)^2 * 2^-43 to break up
 0.00000000000000088817841970012523233890533447265625*
 0.00000000000000088817841970012523233890533447265625*
 0.0000000000001136868377216160297393798828125;
 echo serialize ($dyadic);
?>

This code fails ? it prints

d:4.4841550858394146269559346665277316200968382140048504696226185
08447331464594753924757242202758789062E-44;

<?php
 //Serialize 2^-144
 $dyadic = //Compute as (2^-50)^2 * 2^-44 to break up
 0.00000000000000088817841970012523233890533447265625*
 0.00000000000000088817841970012523233890533447265625*
 0.00000000000005684341886080801486968994140625;
 echo serialize ($dyadic);
?>

This code works ? it prints

0.0000000000009094947017729282379150390625

<?php
 //Serialize and unserialize 2^-40
 $dyadic = 0.0000000000009094947017729282379150390625;
 printf ("%1.40f",unserialize(serialize($dyadic)));
?>

This code fails ? it prints

Fails, prints 0.0000000000004547473508864641189575195312

<?php
 //Serialize and unserialize 2^-41
 $dyadic = 0.00000000000045474735088646411895751953125;
 printf ("%1.41f",unserialize(serialize($dyadic)));
?>
 [2010-11-19 14:17 UTC] iliaa@php.net
-Assigned To: rasmus +Assigned To: iliaa
 [2010-11-19 17:36 UTC] iliaa@php.net
Automatic comment from SVN on behalf of iliaa
Revision: http://svn.php.net/viewvc/?view=revision&amp;revision=305561
Log: Fixed bug #47168 (printf of floating point variable prints maximum of 40 decimal places).
 [2010-11-19 17:36 UTC] iliaa@php.net
-Status: Assigned +Status: Closed
 [2010-11-19 17:36 UTC] iliaa@php.net
This bug has been fixed in SVN.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.


 [2011-03-10 14:25 UTC] eyalt@php.net
Automatic comment from SVN on behalf of eyalt
Revision: http://svn.php.net/viewvc/?view=revision&amp;revision=309072
Log: fixed tests to match the new float precision of 53, as described in bug 47168
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 11:01:29 2024 UTC