0.1 + 0.2 为什么不等于 0.3
这是 JavaScript 及所有用 IEEE 754 浮点数标准的编程语言的常见问题。核心原因:0.1 和 0.2 无法被二进制浮点数精确表示,存储的是近似值,相加后结果与 0.3 的近似值不等。
一、IEEE 754 双精度浮点数结构(64 位)
JavaScript 数字均为 64 位双精度浮点数,结构:
符号位(1bit) | 指数部分(11bits) | 尾数部分(52bits) |
---|---|---|
S | E | M |
数值计算:
- 若 E≠0 且 E≠2047:value = (-1)^S × (1.M)_2 × 2^(E-1023)
- 若 E=0:value = (-1)^S × (0.M)_2 × 2^(-1022)
二、0.1 的二进制表示(不精确)
十进制 0.1 转二进制是无限循环小数: 0.1₁₀ = 0.00011001100110011...₂(循环节 0011)
因尾数仅 52 位,需截断/舍入。实际存储的 0.1 用代码查看:
js
function toIEEE754Bits(num) {
const buffer = new ArrayBuffer(8);
new Float64Array(buffer)[0] = num;
const view = new Uint8Array(buffer);
return Array.from(view).reverse().map(b => b.toString(16).padStart(2, '0')).join('');
}
console.log(toIEEE754Bits(0.1)); // "3fb999999999999a"
解析:
- 符号位 S=0
- 指数 E=0x3FB=1019 → 实际指数=1019-1023=-4
- 尾数 M=0x999999999999A
- 存储值≈(1+0.6)×2^(-4)=0.1(近似值,略大于 0.1)
0.2 同理,存储值略大于 0.2。
三、0.1 + 0.2 的计算过程
CPU 运算步骤:
- 对齐两数指数
- 尾数右移对齐小数点
- 尾数相加
- 规格化结果(可能舍入)
实际结果:
js
0.1 + 0.2; // 0.30000000000000004
其 IEEE 表示为"3fd3333333333334",而 0.3 的表示为"3fd3333333333333",最低位不同,故不等。
四、无法精确表示的原因
1/10 的分母含因子 5,二进制中仅能精确表示分母为 2 的幂的分数(如 1/2、3/8),故 0.1 无法精确存储。
五、如何正确比较
用容差判断:
js
function equal(a, b, epsilon = 1e-15) {
return Math.abs(a - b) < epsilon;
}
console.log(equal(0.1 + 0.2, 0.3)); // true
或用整数运算(如金额以“分”为单位)。
总结
0.1 + 0.2 !== 0.3
是因 IEEE 754 无法精确表示 0.1 和 0.2,相加后误差累积导致。这是浮点数的固有特性,非 JavaScript 漏洞。