php中0.1+0.2==0.3返回false,因ieee 754双精度浮点数无法精确表示十进制小数,导致0.1+0.2实际存储为0.30000000000000004,与0.3内存值不同;bccomp()需传字符串参数,传float会固化误差;比较浮点数应使用abs($a-$b)

PHP中浮点数不能直接用==比较,是因为0.1、0.2、0.3这类常见小数在二进制下无法精确表示,计算结果天然带微小误差——比如0.1 + 0.2实际存的是0.30000000000000004,和0.3的内存值不同,==自然返回false。这不是PHP的bug,而是所有遵循IEEE 754标准的语言共性问题。
为什么0.1 + 0.2 == 0.3返回false
二进制浮点数只能近似表达大部分十进制小数。PHP(以及C、Java、JS等)底层用双精度(64位)存储,0.1在内存中本就是个无限循环二进制小数,存储时被截断,后续运算持续累积误差。
-
printf("%.20f", 0.1 + 0.2);输出0.30000000000000004441 -
printf("%.20f", 0.3);输出0.29999999999999998890 - 两者比特位不同,
==严格比内存值,必然不等
bccomp()必须传字符串,不能传float变量
bccomp()是PHP里最接近“安全比较”的方案,但它只接受字符串参数。一旦你传入0.1这样的字面量或float变量,PHP会先把它转成字符串——而这个转换过程已经把原始浮点误差固化进去了。
- ❌ 错误:
bccomp(0.1 + 0.2, 0.3, 10)→ 实际比较的是"0.30000000000000004"和"0.3" - ✅ 正确:
bccomp("0.1", "0.2", 10)或bccomp("0.30000000000000004", "0.3", 2)(但后者仍需源头可控) - 数据库查出的DECIMAL字段,要用
(string)$row['price']而非(float)再转
什么时候该用abs($a - $b)
当你没法控制输入为字符串(比如函数返回值、中间计算结果、外部API响应),就只能退回到误差容忍法。但阈值$epsilon不是随便写的。
- 通用场景:用
1e-10(double精度足够覆盖多数非金融计算) - 金额类:别硬套
1e-2,优先改用整数单位(如“分”)或bcadd()全程字符串 - 大数比较(如
1e8级别):纯绝对误差失效,需加相对项,例如abs($a - $b) - 永远先检查
is_nan($a) || is_nan($b),否则abs(NaN - $b)仍是NaN,整个条件失效
对象属性、JSON解析、循环条件里的隐藏陷阱
浮点误差在面向对象或数据流转中更难察觉,因为“看起来一样”的值在内存里可能差几个最低有效位。
- DTO对象的
price属性来自JSON:PHP默认把{"price":99.99}转成float,再转字符串就失真;应启用JSON_BIGINT_AS_STRING并手动处理小数字段 - 数据库
SELECT price FROM goods WHERE price = 99.99:如果字段是FLOAT,查询可能命中失败;必须用DECIMAL(10,2)+ 参数绑定字符串 - 循环写
for ($x = 0.0; $x != 1.0; $x += 0.1):$x永远到不了精确的1.0,大概率死循环;应改用整数计数器
真正麻烦的从来不是“怎么写判断”,而是“在哪一步开始丢失精度”——从用户输入、JSON解析、数据库驱动、中间计算到最终比较,每个环节都可能悄悄塞进一个1e-16的偏差。守住字符串源头,比在末端补round()或调$epsilon可靠得多。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











