为什么PHP';s sprintf不是第五轮吗?

为什么PHP';s sprintf不是第五轮吗?,php,printf,rounding,Php,Printf,Rounding,我依赖的是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

我依赖的是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
php>用于($j=0;$j 0.1//以上,如预期
1.05->1.1//如预期向上
2.05->2.0//向下!
3.05->3.0//向下!
4.05->4.0//向下!
5.05->5.0//向下!
6.05->6.0//向下!
7.05->7.0//向下!
8.05->8.1//如预期向上
9.05->9.1//如预期向上

我完全没有抓住要点吗?我感觉就像一块毯子被从我身上拉开了,我在学校学到的一切都是错误的…!当然,对数字进行四舍五入的函数应该始终如一地做到这一点?(我注意到,
round($n,1)
的效果与预期一样。)

你应该使用
round


在回答中总结评论中所说的话:

因为
printf
不是一个舍入函数,而是一个给出数字字符串表示的低级函数。在这种情况下,数字内部与数字集不同,例如,由于浮点内部表示的限制,它可能是
2.24999991231123

因此,
printf
将一个不同的数字四舍五入到您输入/计算的数字,这在本例中是正确的
2.2


因此,正如另一个答案(以及我原来的问题)所指出的,更好的解决方案是使用
round()
(可能还有
sprintf

< p>作为解释为什么<代码>圆< /代码>可以提供比没有专门设计舍入的函数更好的结果,我们需要考虑双精度浮点表示的限制。双倍可以表示15和17之间的十进制数和数字。 如果将最多具有15个有效数字的十进制字符串转换为IEEE 754双精度表示,然后再转换回具有相同有效数字数的字符串,则最终字符串应与原始字符串匹配。如果将IEEE 754双精度转换为具有至少17个有效数字的十进制字符串,并且然后转换回双精度,则最终数字必须与原始数字匹配

在大多数情况下,
round
的实现可以而且应该利用它来“做正确的事情”

例1:

<?=number_format(2.05,14); //give me 15 significant digits. Guaranteed to produce the orginal number
例2:

<?=number_format(2.05,16); //give me 17 significant digits. Not guaranteed to produce the orginal number
这只是IEEE 754行为的一个演示


我想(因为我没有读过它的实现),
sprintf
并没有真正尝试做任何关于舍入的特别智能的事情,而
round
可能尝试“正确”舍入(根据IEEE 754)关于您要求的有效位数。

因为2.05在内部实际上是2.049999123423或其他什么。您处理的是浮点数。精度不是他们的强项。
当然,对数字进行舍入的函数应该始终如一地做到这一点。
。如果它实际上是对数字进行舍入的函数。但它不是-
sprintf-返回一个格式化的字符串
,如.importional@MarcB Get it。你怎么知道呢?我的意思是obvs你可以手动计算出浮点数的二进制表示形式,但是你怎么能让PHP输出2.049999123(或其他什么)?-只是出于兴趣!而且读起来也不错。不过我认为浮点运算非常难…如果你在读了所有这些东西后没有真正理解它,也不要感到难过。但它的缺点是,二进制中的一些数字是重复数,而另一些则不是。有点像没有inf,你不能准确地将分数1/3写成十进制初始数字,但1/4很容易。如果这些重复数字恰好发生在你想要取整的地方,那么它们的截断可能会导致令人惊讶的结果。很好的解释,谢谢。你认为如果我把这个问题表述为“为什么php的printf不是特别智能”的话,我可能不会被-2击垮吗?;-)@我不确定,因为我没有投反对票。有些人似乎对“幼稚”的解决方案反应消极,特别是当类似的事情经常出现时。关于浮点数,有很多问题,我认为长期以来的读者都有一定程度的“疲劳”。
2.05000000000000
<?=number_format(2.05,16); //give me 17 significant digits. Not guaranteed to produce the orginal number
2.0499999999999998