0.1 + 0.2 !== 0.3 是因 IEEE 754 浮点精度限制,非 PHP 特有;须全程用字符串调用 bc 函数,显式指定 $scale,避免隐式转换和全局 bcscale 误用。

为什么 0.1 + 0.2 !== 0.3 在 PHP 里也成立
不是 PHP 特有 bug,是所有遵循 IEEE 754 的语言共性:二进制无法精确表示大部分十进制小数。0.1 在内存里存的是近似值,加减后误差累积,直接用 == 或 === 判断会翻车。
常见错误现象:bcadd('0.1', '0.2', 1) === '0.3' 返回 false(因为结果是 '0.3' 字符串,而右边是浮点数);或用 round($a + $b, 2) === 0.3 仍不等——round 不解决底层精度,只修显示。
- 必须全程用字符串传参给
bc函数,不能传 float 变量(如bcadd(0.1, 0.2, 2)会先让 PHP 把0.1转成不精确的 float 再转字符串,白忙) - 所有参与计算的数字,包括常量,都得写成字符串:用
'0.1',别用0.1 -
bc系列函数返回值永远是字符串,比较时两边都得是字符串,或显式转floatval()(但仅限展示/简单判断,别再拿去算)
bcadd / bcsub / bcmul / bcdiv 参数怎么设才不踩坑
四个函数签名一致:bcxxx(string $left, string $right, int $scale = 0)。关键在 $scale:它指定结果保留几位小数,**不是四舍五入位数,而是强制截断到该精度**(bcdiv('1', '3', 2) 得 '0.33',不是 '0.34')。
使用场景:金融计算要求确定性,比如价格、余额、汇率换算——这些地方不能依赖 round() 或 number_format() 临时补救。
-
$scale必须显式传,别依赖默认值0(整数运算),否则除法直接丢小数,乘法可能失精度 - 加减法的
$scale应取两个操作数中小数位数的最大值(如'1.23'+'4.567',设3);乘法则建议设为两数小数位之和('1.23' * '4.567'→3 + 3 = 6) -
bcdiv的$scale如果设太小(如0),bcdiv('5', '3', 0)返回'1',不是'2',它截断,不四舍五入
什么时候不该硬上 bc 函数
bc 是字符串模拟运算,比原生 float 慢一个数量级以上,且不能直接参与数组排序、JSON 编码(会变成字符串)、数据库插入(需再转类型)。
容易被忽略的兼容性问题:PHP 默认不启用 bcmath 扩展,Docker 镜像、Alpine 环境、部分共享主机可能没装,function_exists('bcadd') 得先检查。
- 纯前端展示型计算(如价格预估、非交易类表单校验),用
round($x, 2)+ 严格小数位格式化更轻量 - 涉及大量循环计算(如千条订单逐笔累加),优先考虑数据库层用
DECIMAL字段 + SQL 运算,避免 PHP 层反复调用bc - 如果项目已用 ORM,且字段定义为
DECIMAL(10,2),PHP 层读出来是字符串或 float,但只要不参与二次计算,直接透传即可,不必强转bc
bcscale 全局设置有没有用
有用,但容易误用。bcscale(2) 会把后续所有 bc 函数的默认 $scale 设为 2,但它**不改变已有函数调用中显式传入的 $scale**(即 bcadd('1','2',5) 还是按 5 算)。
真实痛点:多人协作时,有人在文件头写了 bcscale(2),另一人在中间调用 bcdiv('1','3') 没传 $scale,结果得 '0.33';但第三个人在别处又调 bcscale(6),全局就变了——行为不可控。
- 推荐始终显式传
$scale,别依赖bcscale(),尤其在函数/方法内部 - 如果真要用全局设置,确保它在入口文件最早执行,且整个请求生命周期内不再变更
-
bcscale()设置的是“默认值”,不是“强制值”,这点文档写得模糊,很多人以为设了就一劳永逸
事情说清了就结束
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











