PHP 浮点数精度问题是什么?如何用 bcmath 解决?
PHP 浮点数精度问题
PHP 遵循 IEEE 754 标准,浮点数用二进制存储,无法精确表示某些十进制小数,导致计算结果出现偏差:
var_dump(0.1 + 0.2 == 0.3); // false
var_dump(0.1 + 0.2); // float(0.30000000000000004)
var_dump(0.3 - 0.2 == 0.1); // false
var_dump(0.3 - 0.2); // float(0.09999999999999998)
常见受影响场景:金额计算、价格比较、库存扣减等对精度要求高的业务。
用 bcmath 解决
bcmath 支持任意精度的数学运算,数字以字符串形式参与计算,不会丢失精度。
基本用法
// 加法
bcadd('0.1', '0.2', 2); // "0.30"
// 减法
bcsub('0.3', '0.2', 2); // "0.10"
// 乘法
bcmul('0.1', '0.2', 4); // "0.0200"
// 除法
bcdiv('1', '3', 6); // "0.333333"
// 比较(返回 0 表示相等)
bccomp('0.3', bcadd('0.1', '0.2', 2), 2); // 0(相等)
// 取余
bcmod('10', '3'); // "1"
// 幂运算
bcpow('2', '10'); // "1024"
参数说明
- 前两个参数:操作数(必须传字符串)
- 第三个参数:小数位数(精度),不传则使用默认值
设置全局精度
// 设置默认小数位数为 4
bcscale(4);
bcdiv('1', '3'); // "0.3333"(无需每次传精度)
实际应用场景:金额计算
// 错误做法
$total = 0.1 + 0.2; // 0.30000000000000004
// 正确做法
$total = bcadd('0.1', '0.2', 2); // "0.30"
// 金额比较
if (bccomp($price, '0.00', 2) > 0) {
echo '价格大于0';
}
其他解决方式对比
| 方式 | 适用场景 | 示例 |
|---|---|---|
bcmath |
高精度计算(推荐) | bcadd('0.1', '0.2', 2) |
| 转为整数计算 | 金额(分) | (10 + 20) / 100 |
round() |
简单展示 | round(0.1 + 0.2, 2) |
number_format() |
格式化输出 | number_format(0.1+0.2, 2) |
总结:涉及金额、财务等精确计算时,务必用 bcmath 并以字符串传参。round() 只是"看起来对了",底层精度问题依然存在,不适合做逻辑判断。