PHP中双精度数值相等但比较返回FALSE问题问询
为什么MySQL的double与decimal求和结果在PHP中比较不相等?
核心问题解析
你遇到的问题本质是二进制浮点数的精度误差,以及MySQL不同数值类型在PHP中的处理逻辑差异导致的:
- 从
double(7,3)字段求和得到的$totalPrice,PHP解析为float类型后,实际值是39.669000000000004(而非表面显示的39.669) - 从
decimal(20,3)字段求和得到的$totalFinalPrice,PHP默认以字符串返回精确值"39.669",强制转float后得到的近似值和前者的微小误差,直接导致比较失败
原因拆解
MySQL字段类型的本质区别
double是二进制浮点型:存储的是十进制数的二进制近似值,即使定义了(7,3)的精度,底层仍会存在微小误差,求和操作会放大这种误差decimal是精确数值类型:以字符串形式存储十进制数,求和后返回的是精确的十进制字符串,不会产生浮点误差
PHP的类型转换逻辑
- 读取MySQL的
double结果时,PHP直接将二进制浮点值映射为自身的float/double类型,保留了底层的精度误差 - 读取MySQL的
decimal结果时,PHP默认以字符串返回(避免精度丢失),当强制转为float时,PHP会将字符串"39.669"解析为最接近的二进制浮点值,但这个值和double字段求和后得到的浮点值并非完全一致
- 读取MySQL的
验证精度差异的方法
用高精度格式化输出就能看到真实值:
// 查看$totalPrice的真实精度 var_dump(sprintf('%.20f', $totalPrice)); // 输出 39.66900000000000400000 // 查看强制转float后的$totalFinalPrice真实精度 var_dump(sprintf('%.20f', (float)$totalFinalPrice)); // 输出 39.66899999999999700000(或近似的精确表示)
正确的比较方式
- 直接用字符串比较:如果
$totalFinalPrice是decimal返回的字符串,将$totalPrice格式化后再比较:
if (sprintf('%.3f', $totalPrice) === $totalFinalPrice) { // 相等逻辑 }
- 使用高精度函数比较:用PHP的BCMath扩展处理精确数值比较:
// 保留3位小数进行比较 if (bccomp((string)$totalPrice, $totalFinalPrice, 3) === 0) { // 相等逻辑 }
- 在MySQL层面统一类型:查询时将double的求和结果转为decimal,避免PHP端的精度问题:
SELECT CAST(SUM(price) AS DECIMAL(20,3)) AS total_price FROM Packs; SELECT SUM(final_price) AS total_final_price FROM Contracts;
此时PHP读取到的两个值都是字符串类型的精确值,直接比较即可。
内容的提问来源于stack exchange,提问作者guyaloni
相关产品推荐
相关产品推荐

