PHP浮点值比较异常排查:数值相同却判定不等
浮点数精度陷阱导致的“明明相等却判定不等”问题
哦,这个坑我踩过好多次——这就是典型的浮点数精度问题!你看到的打印值是19.6,但实际上这两个变量在内存里存储的二进制浮点数可能有极其微小的差异,只是echo的时候自动帮你做了四舍五入,把差异藏起来了而已。
为什么会这样?
十进制的小数(比如19.6)没法用二进制浮点数精确表示,就像你没法用十进制精确表示1/3(只能写成0.333...无限循环)一样。19.6转换成二进制后是一个无限循环的小数,存储的时候会被截断到浮点数的精度上限(PHP里的float是双精度64位),所以实际存储的值可能是19.599999999999998或者19.600000000000001这种,虽然看起来和19.6一样,但本质上是不同的数值。
你用floatval()转换也没用,因为不管怎么转,只要是浮点数存储,这个精度损失就存在。
怎么解决?
针对这种问题,有两种常用的解决方案,根据你的业务场景选择:
1. 用误差范围(Epsilon)比较
不要直接用!=判断,而是判断两个数的差的绝对值是否小于一个极小的阈值(比如0.0001,根据你的业务精度调整,金额场景用0.0001就足够):
// 定义可接受的误差范围 $epsilon = 0.0001; $totalFloat = floatval($total); $dbTotalFloat = floatval($r['sum_total']); $subTotalFloat = floatval($subTotal); $dbSubTotalFloat = floatval($r['sum_sub']); if (abs($totalFloat - $dbTotalFloat) > $epsilon || abs($subTotalFloat - $dbSubTotalFloat) > $epsilon) { echo 'Differs on #' . $r['id']; echo 'Total: ' . $total . ' / ' . $r['sum_total']; echo 'Sub: ' . $subTotal . ' / ' . $r['sum_sub']; }
2. 用整数存储/计算(推荐金额场景)
如果是处理金额这类对精度要求高的数值,最好把所有数值转成整数(比如把元转成分,19.6元变成1960分),这样就完全避免了浮点数的精度问题:
// 把金额转成整数(保留两位小数的话乘100,用round避免截断误差) $totalInt = round($total * 100); $dbTotalInt = round(floatval($r['sum_total']) * 100); $subTotalInt = round($subTotal * 100); $dbSubTotalInt = round(floatval($r['sum_sub']) * 100); if ($totalInt != $dbTotalInt || $subTotalInt != $dbSubTotalInt) { echo 'Differs on #' . $r['id']; echo 'Total: ' . $total . ' / ' . $r['sum_total']; echo 'Sub: ' . $subTotal . ' / ' . $r['sum_sub']; }
验证真实差异
你可以用var_dump()代替echo来查看变量的真实值,比如:
var_dump($total, $r['sum_total']);
这时候你会看到类似这样的输出:
float(19.599999999999998) float(19.6)
就能直观看到它们的差异了。
内容的提问来源于stack exchange,提问作者SQRCAT
相关产品推荐
相关产品推荐

