参考:php中sprintf('%.2f', $str)的一个让人无法理解的问题?
首先看一段代码示例
echo sprintf('%.2f',123.455);
// 123.45
echo sprintf('%.2f',12.455);
// 12.46
PHP 的 sprintf('%.2f', $str) 通常被认为是四舍五入,应该都是输出 0.46 才对;但是两个结果不同,一个 0.46 一个 0.45,为什么呢?
sprintf('%.2f', $str) 的“四舍五入”问题
在 PHP 中 sprintf 经常被用来格式化数据,当 sprintf 用 %.*f 格式化时经常被认为是“四舍五入”,但实事上并不是数学上的“四舍五入”,而是“四舍六入五成双”。
(1)当保留精度的下一位非 5 时,按正常的四舍五入;
(2)当保留精度的下一位是 5 时,5 的前一位是奇数则进位,是偶数则舍弃;
(3)若 5 的后面还有非 0 的部分,则无论 5 的前一位是奇数还是偶数,都进行进位;
1.当保留精度的下一位不是 5 时,按正常的四舍五入;
echo sprintf('%.2f', 12.464);
// out 12.46
echo sprintf('%.2f', 12.466);
// out 12.47
2.当保留精度的下一位是 5 时,5 的前一位是奇数则进位,是偶数则舍弃;
echo sprintf('%.2f', 12.465);
// out 12.46
echo sprintf('%.2f', 12.455);
// out 12.46
3.若 5 的后面还有非 0 的部分,则无论 5 的前一位是奇数还是偶数,都进行进位;
echo sprintf('%.2f', 12.4651);
// out 12.47
解释最初的示例代码
十进制浮点数在计算机内部也是用二进制表示的,有限位置的 十进制浮点数 转换成二进制很有可能是无限循环的数。如:
// 123.455
1111011.01110100011110101110000101...101
将 123.455 转换成二进制就会发现 00011110101110000101 是循环的,计算机不可能保存一个无限长的数字,双精度浮点数 下使用 64 位(8字节)来存储一个浮点数,其中小数位有 53 位。53 位之后的被舍去了,这样就变成有限位,但是也带来了精度问题。
echo printf('%.53f',123.455);
// 123.45499999999999829469743417575955390930175781250000000
echo printf('%.53f',12.455);
// 12.45500000000000007105427357601001858711242675781250000
将全部 53 位小数显示出来你会发现,123.455 其实是 123.454999.... 第三位其实是 4 并不是 5,所以直接被舍去了。这个是浮点数精度问题,同样存在于其他语言中。
echo sprintf('%.2f',123.455);
// 123.45
echo sprintf('%.2f',12.455);
// 12.46
在 PHP 中可以使用 round 方法来进行 四舍五入
echo round(123.455, 2);
// 123.46
echo round(12.455, 2);
// 12.46