0.1 + 0.2 ≠ 0.3 不是 bug,而是 ieee 754 浮点数在有限位宽下无法精确表示十进制小数 0.1、0.2(二者二进制均为无限循环小数)所致,截断舍入后误差累积,导致结果为 0.30000000000000004。

0.1 + 0.2 ≠ 0.3 不是 bug,而是 IEEE 754 浮点数表示法在有限位宽下必然发生的数学事实。核心原因在于:十进制小数无法被精确映射到二进制有限位浮点格式中。
十进制小数在二进制中本就是无限循环的
计算机用二进制存储一切数字,但很多常用十进制小数(如 0.1、0.2、0.3)在二进制中是无限不循环小数,类似十进制里的 1/3 = 0.333…。例如:
- 0.1₁₀ = 0.00011001100110011…₂(“0011”无限循环)
- 0.2₁₀ = 0.0011001100110011…₂
这些无限位二进制小数必须被截断或舍入,才能塞进 IEEE 754 规定的尾数位中(float 23 位,double 52 位),于是第一次误差就已产生。
IEEE 754 的结构放大了精度限制
它把一个数拆成三部分:符号位 + 指数位 + 尾数位。关键细节在于:
- 尾数采用“隐含前导 1”设计(规格化数),所以 float 实际有 24 位二进制精度,double 有 53 位
- 换算成十进制,float 约 6–7 位有效数字,double 约 15–16 位——但这不是小数点后几位,而是整数+小数的总有效位数
- 数值越大,可表示的相邻两个浮点数间距(ULP)越大,精度越“稀疏”。比如 double 存 9990000.123456789 时,整数占 7 位,小数最多只剩约 8 位可靠精度
运算过程会累积并暴露误差
单个浮点数已有微小偏差,四则运算还会引入新误差:
- 对阶丢失低位:计算 1e17 + 1 时,1 会被直接抹掉(因指数差太大,低位无法对齐进尾数)
- 舍入规则影响结果:IEEE 754 默认使用“向偶数舍入”,不是简单四舍五入,导致看似随机的偏差
-
比较失效:用 == 判断浮点数相等几乎总是错的;应改用
Math.abs(a - b)
哪些场景要特别警惕
不是所有计算都受同等影响,以下情况误差更易显现或造成后果:
- 金融系统中用 float/double 存金额(正确做法:用整数分/厘,或 decimal 类型)
- 地理坐标叠加高精度小数(如经度 116.12345678912345678 → 实际存为 116.12345678912345)
- 长期迭代累加(如物理模拟、传感器积分),误差随次数线性或指数增长
- PLC 或嵌入式设备用二进制协议读写 float,字节序或类型误配可能让 618.0 变成 608.0











