PHP 8.1与PHP 7浮点数计算结果不一致问题咨询
问题本质
你观察到的输出差异并非PHP 8.1的浮点数计算逻辑变更导致,而是两个版本的浮点数默认输出规则不同:
- 二进制体系下浮点数本身无法精确表示绝大多数十进制小数,你给出的
1097.5 - 835.7计算结果,在PHP7和PHP8.1环境的底层存储值完全一致 - PHP7默认的
precision配置(控制浮点数输出、转字符串时的有效数字位数)为14,输出时会自动对超出位数的部分做四舍五入,因此你看到的结果是float(261.8) - PHP8.0+调整了浮点数输出逻辑,默认会展示浮点数的完整精确存储值,因此你会看到
float(261.79999999999995)
低改造成本解决方案
方案1:调整PHP配置还原PHP7运行效果
无需修改业务计算逻辑,仅需调整两处配置即可让浮点数的输出、转字符串、序列化逻辑和PHP7完全一致:
- 直接修改php.ini配置:
precision = 14 serialize_precision = -1
- 若没有修改php.ini权限,可以在项目全局入口文件最顶部添加两行代码:
ini_set('precision', 14); ini_set('serialize_precision', -1);
如果你的原有判断逻辑依赖浮点数转字符串后的结果(例如(string)$balance == '0'),调整配置后即可直接恢复PHP7的运行效果。
方案2:统一相等判断逻辑(适配直接比较浮点数的场景)
如果你的判断逻辑是直接对浮点数做== 0/=== 0比较,调整配置无法解决底层存储的精度误差问题,你可以封装一个全局判断函数,全局搜索替换所有余额判0逻辑即可,无需修改每一步计算:
function is_balance_zero(float $balance): bool { // 会计场景一般保留2位小数,设置1e-4的误差阈值即可覆盖绝大多数计算误差 return abs($balance) < 0.0001; }
使用时把原有的$balance == 0替换为is_balance_zero($balance)即可,修改成本极低。
长期优化建议
会计类涉及金额计算的场景,更推荐使用BC Math扩展做任意精度的十进制计算,所有计算统一用bcadd、bcsub、bcmul、bcdiv等函数,固定保留2位小数(单位为元)或保留到整数(单位为分),可从根源杜绝浮点数精度问题。
内容的提问来源于stack exchange,提问作者Sylvain Martens
相关产品推荐
相关产品推荐

