我依赖于sprintf('%0.1f', 2.25) === '2.3'
,但事实证明它出现在2.2
!
事实上它似乎是随机的:
php > for ($j=0;$j<=10;$j++) { printf( "%s -> %0.1f\n",$j+ 0.05, $j+0.05); }
0.05 -> 0.1 // Up, as expected
1.05 -> 1.1 // Up, as expected
2.05 -> 2.0 // Down!
3.05 -> 3.0 // Down!
4.05 -> 4.0 // Down!
5.05 -> 5.0 // Down!
6.05 -> 6.0 // Down!
7.05 -> 7.0 // Down!
8.05 -> 8.1 // Up, as expected
9.05 -> 9.1 // Up, as expected
我完全错过了这一点吗?我觉得地毯正在从我身下被拉开,我在学校学到的都是错的......!当然,圆形数字的功能应该一致吗? (我注意到round($n, 1)
按预期工作。)
答案 0 :(得分:4)
答案 1 :(得分:3)
总结答案中评论中所说的内容:
因为printf
不是舍入函数,而是一个低级函数,用于给出数字的字符串表示。在这种情况下,内部的数字与设置的数字不同,例如它可能是2.249999991231231123
,因为浮点内部表示的局限性。
因此,printf
将您输入/计算的数字舍入不同的数字,在此示例中正确2.2
。
因此,正如另一个答案(以及我原来的问题)所指出的,更好的解决方案是在结果上使用round()
(可能还有sprintf
)。
答案 2 :(得分:2)
为了解释为什么round
可以提供比没有专门设计用于舍入的函数更好的结果,我们需要考虑双精度浮点表示的限制。双打可以表示15到17个十进制有效数字。来自Wikipedia article on double precision:
如果具有最多15位有效数字的十进制字符串转换为IEEE 754双精度表示,然后转换回具有相同有效位数的字符串,则最终字符串应与原始字符串匹配。如果IEEE 754双精度转换为具有至少17位有效数字的十进制字符串,然后转换回双精度数,则最终数字必须与原始数字匹配
round
的实施可以而且应该利用这个来做正确的事情&#34;在大多数情况下。
示例1:
<?=number_format(2.05,14); //give me 15 significant digits. Guaranteed to produce the orginal number
输出:
2.05000000000000
示例2:
<?=number_format(2.05,16); //give me 17 significant digits. Not guaranteed to produce the orginal number
输出:
2.0499999999999998
这只是IEEE 754行为的示范。
我将猜测(因为我还没有读到它的实现),sprintf
并没有真正尝试做一些特别聪明的关于舍入的事情,而round
可能试图正确地绕#34; (根据IEEE 754)关于您要求的有效位数。