php中sprintf使用bug

前言

最近在用sprintf函数进行四舍五入数据处理的时候的时候,发现一个问题。

echo sprintf('%.2f',123.455); //123.45
echo sprintf('%.2f',12.455);//12.46

上面的sprintf(’%.2f’, $str)是被常认为的四舍五入,但是两个结果截然不同,按照正常情况应该是是输出X.46才对,怎么一个X.46一个X.45呢。

sprintf(’%.2f’, $str) 的“四舍五入”输出的问题

在php中sprintf经常被用来格式字符串,当sprintf用%.*f格式化时经常被认为是“四舍五入”,但实事上这个“四舍五入”并不是数学上的“四舍五入”,而是“四舍六入五成双”,英文中被称为“round half to even”或”Banker’s rounding”。
“四舍六入五成双”是指,当保留精度的下一位不是5时,按正常的四舍五入;当保留精度的下一位是5时,5的前一位是奇数,则进位,是偶数,则舍弃;而如果5的后面还有大于0的部分时,则无论5的前一位是奇数还是偶数,都进行进位。

1.当保留精度的下一位不是5时,按正常的四舍五入。
$str = 12.464;
echo sprintf('%.2f', $str)//out 12.46
2.当保留精度的下一位是5时,5的前一位是奇数,则进位,是偶数,则舍弃。
//前一位是偶数:
$str = 12.465;
echo sprintf('%.2f', $str)
//out 12.46
//前一位是奇数:
$str = 12.455;
echo sprintf('%.2f', $str)
//out 12.46
3.而如果5的后面还有大于0的部分时,则无论5的前一位是奇数还是偶数,都进行进位。
$str = 12.4651;
echo sprintf('%.2f', $str)
//out 12.47

现在来说说我最开始列出的代码:

echo sprintf('%.2f',123.455); //123.45
echo sprintf('%.2f',12.455);//12.46

按照这篇文章所讲的四舍六入五成双,“四舍六入五成双”是指,当保留精度的下一位不是5时,按正常的四舍五入;当保留精度的下一位是5时,5的前一位是奇数,则进位,是偶数,则舍弃;而如果5的后面还有大于0的部分时,则无论5的前一位是奇数还是偶数,都进行进位。
这句话,来分析第一行代码,

echo sprintf('%.2f',123.455); //123.45

当保留精度的下一位是5时,5的前一位是奇数,则进位,是偶数,则舍弃;123.455,它的下一位是5,应该是进取,输出123.46才对,但是却输出123.45,非常不理解。
现在来说说第二行代码,

echo sprintf('%.2f',12.455);//12.46

12.455,它的下一位是5,进取,输出12.46,没有错误,属于正确的,但是上面这行代码却不是这个逻辑。

原因

因为十进制浮点数在计算机内部也是用二进制表示的,有限位置的十进制浮点数转换成二进制很有可能是无限循环的数。如:

1111011.0111010001111010111000010100011110101110000101...00011110101110000101//123.455

将123.455转换成二进制就会发现00011110101110000101是循环的,计算机不可能保存一个无限长的数字,双精度浮点数下使用 64 位(8字节) 来存储一个浮点数,其中小数位有53位。53位之后的被舍去了,这样就变成有限位,但是也带来了精度问题。

php -r "printf('%.53f',12.455);"
php -r "printf('%.53f',123.455);"

在这里插入图片描述
将全部53位小数显示出来你会发现,123.455其实是123.454999…第三位其实是4并不是5 所以直接被舍去了。而12.455全部53位,这个是浮点数精度问题,同样存在于python等其他语言中。
在PHP中可以使用 “round”方法来进行四舍五入。
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值