|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2011-02-03 12:56 UTC] exploringbinary at gmail dot com
Description: ------------ Bug #47168, "printf of floating point variable prints maximum of 40 decimal places", was not fixed as expected. Instead of the previous arbitrary limit of 40 digits, there is now an arbitrary limit of 53 digits. These three examples all print powers of two, which have exact representations in double-precision floating-point: <?php printf("%1.176f\n",1.044048714879763924273647057481047608912186281291034647641381832875155719135597796355663380296618925058282911777496337890625e-53); /* 2^-176 */?> <?php printf("%1.177f\n",5.220243574398819621368235287405238044560931406455173238206909164375778595677988981778316901483094625291414558887481689453125e-54); /* 2^-177 */?> <?php printf("%1.178f\n",2.6101217871994098106841176437026190222804657032275866191034545821878892978389944908891584507415473126457072794437408447265625e-54); /* 2^-178 */?> The output is: 0.00000000000000000000000000000000000000000000000000001 0.00000000000000000000000000000000000000000000000000001 0.00000000000000000000000000000000000000000000000000000 (The first prints 1 significant digit, the second rounds to 1 significant digit, and the third prints no significant digits.) Compare this to gcc C on Linux: printf("%1.176f\n",1.044048714879763924273647057481047608912186281291034647641381832875155719135597796355663380296618925058282911777496337890625e-53); /* 2^-176 */ printf("%1.177f\n",5.220243574398819621368235287405238044560931406455173238206909164375778595677988981778316901483094625291414558887481689453125e-54); /* 2^-177 */ printf("%1.178f\n",2.6101217871994098106841176437026190222804657032275866191034545821878892978389944908891584507415473126457072794437408447265625e-54); /* 2^-178 */ The output (which is correct) is: 0.00000000000000000000000000000000000000000000000000001044048714879763924273647057481047608912186281291034647641381832875155719135597796355663380296618925058282911777496337890625 0.000000000000000000000000000000000000000000000000000005220243574398819621368235287405238044560931406455173238206909164375778595677988981778316901483094625291414558887481689453125 0.0000000000000000000000000000000000000000000000000000026101217871994098106841176437026190222804657032275866191034545821878892978389944908891584507415473126457072794437408447265625 (I see that in formatted_print.c, #define MAX_FLOAT_PRECISION 40 was changed to #define MAX_FLOAT_PRECISION 53) Test script: --------------- <?php printf("%1.178f\n",2.6101217871994098106841176437026190222804657032275866191034545821878892978389944908891584507415473126457072794437408447265625e-54); /* 2^-178 */?> Expected result: ---------------- All requested decimal places are printed. Actual result: -------------- Only 53 decimal places are printed. PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sat Nov 08 21:00:01 2025 UTC |
> I'm not sure what that formula tells you. 2^-1022 has 1022 > decimal digits: 307 leading 0s followed by 715 other digits. Yes, but not all digits are born the same. The accuracy of the IEEE double that represents 2^-1022 is 323.607. That means all the decimal digits beyond that could be wrong due to rounding errors. Eith uncertainty dx, Accuracy[x] is -Log[10,dx]. For accuracy = 323.607, dx = 10^-323.607 = 2.470328229206*10^-324. Which means that, unless you're sure your number is actually exactly represented in an IEEE double (an unlikely scenario of application), your IEEE double doesn't represent 2^-1022. It represents 2^-1022 ± 1.235164114603*10^-324. You can easily see this with a small C program. Let's take our 2^1022, which has a nice binary representation: #include<stdio.h> void main() { double u,v; u = 2.22507385850720138309023271733e-308; /* given with 30 digits */ v = 2.2250738585072014e-308; /* given with 17 digits */ printf("%016llx %016llx\n", *((unsigned long long*)&u), *((unsigned long long*)&v)); } This prints the same double in big-endian form: 0010000000000000 0010000000000000