这不是 javascript 的 bug,而是 ieee 754 双精度标准下十进制小数无法在二进制中精确表示所致:0.1 和 0.2 均为无限循环二进制小数,存储时被截断产生固有误差,相加后不等于 0.3。

这不是 JavaScript 的 bug,而是所有遵循 IEEE 754 双精度标准的语言共有的确定性行为。根本原因在于:十进制小数 0.1 和 0.2 在二进制中无法被有限位精确表示,计算机只能存储它们的近似值,相加后误差叠加,结果自然不等于数学上理想的 0.3。
十进制小数在二进制中“天生失真”
计算机用二进制(base-2)运算和存储,而人类习惯的十进制小数要转成二进制时,很多会变成无限循环小数:
- 0.1(十进制)= 0.0001100110011…₂(无限循环,“0011”不断重复)
- 0.2(十进制)= 0.001100110011…₂(同样无限循环)
- 就像你无法用有限个 1/3 相加得到精确的 1,计算机也无法用有限个 1/2、1/4、1/8… 拼出精确的 0.1
IEEE 754 双精度强制“截断近似”
JavaScript 所有数字都按 64 位双精度浮点格式存储,其中仅 52 位用于尾数(有效数字),必须把无限长的二进制小数硬生生截断到 52 位——这就引入了固有误差:
- 0.1 实际存的是 ≈ 0.100000000000000005551115…(比真实值略大)
- 0.2 实际存的是 ≈ 0.200000000000000011102230…(也略大)
- 两者相加再经舍入规则(round-to-nearest, ties-to-even)处理,最终得 ≈ 0.300000000000000044408920…
看似“正确”的操作往往只是巧合
比如 (0.1 * 10 + 0.2 * 10) / 10 得到 0.3,并非方法可靠,而是因为:
- 0.1 × 10 = 1.0、0.2 × 10 = 2.0 —— 这两个乘积恰好能被双精度精确表示
- 1.0 + 2.0 = 3.0 仍是精确整数,除以 10 也未引入新误差
- 但换成
(0.3 * 10 + 0.6 * 10) / 10就失败:0.3 × 10 实为 3.0000000000000004,0.6 × 10 为 6.000000000000001,相加再除 10 后变成 0.8999999999999999
识别陷阱的关键信号
以下现象出现时,大概率已落入浮点陷阱:
- 小数参与的
===或==比较返回false(如0.1 + 0.2 === 0.3) - 累加或多次运算后,结果末尾出现一长串
00004、99999等非预期数字 -
.toFixed(n)表现异常(如(1.45).toFixed(1)得"1.4"而非"1.5") - 金额计算中余额显示为
29.999999999999996而非30
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











