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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: exploringbinary at gmail dot com
New email:
PHP Version: OS:

 

 [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-2025 The PHP Group
All rights reserved.
Last updated: Tue Feb 04 03:01:31 2025 UTC