这是一个经典的 JavaScript 浮点数精度问题。
一、为什么 0.1 + 0.2 !== 0.3
?
根本原因:二进制浮点数的精度限制
JavaScript 使用 IEEE 754 标准的 64 位双精度浮点数来表示数字。这种格式无法精确表示某些十进制小数(如 0.1
和 0.2
),因为它们在二进制中是无限循环小数。
0.1
(十进制)在二进制中是0.0001100110011...
(无限循环)0.2
(十进制)在二进制中是0.001100110011...
(无限循环)
由于计算机内存有限,这些无限循环的小数必须被截断或舍入,导致微小的精度损失。
实际计算结果:
console.log(0.1 + 0.2);
// 输出:0.30000000000000004
console.log(0.1 + 0.2 === 0.3);
// 输出:false
0.1 + 0.2
的结果并不是精确的 0.3
,而是略大于 0.3
的 0.30000000000000004
。
二、如何让 0.1 + 0.2
等于 0.3
?
方法 1:使用 Number.EPSILON
进行近似比较(推荐)
Number.EPSILON
表示 JavaScript 中可以表示的最小差值(约为 2.22e-16
)。我们可以利用它来判断两个浮点数是否“足够接近”。
function isEqual(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
isEqual(0.1 + 0.2, 0.3); // true
⚠️ 注意:
Number.EPSILON
非常小,适用于大多数情况,但在涉及较大数字时可能需要调整容差。
方法 2:转换为整数进行计算
将小数乘以 10 的幂次,转换为整数运算,再除回去。
const result = (0.1 * 10 + 0.2 * 10) / 10; // (1 + 2) / 10 = 0.3
console.log(result === 0.3); // true
适用于已知小数位数的场景(如货币计算,以“分”为单位)。
方法 3:使用 toFixed()
并转换回数字
const result = parseFloat((0.1 + 0.2).toFixed(10));
console.log(result === 0.3); // true
toFixed(n)
将数字转换为指定小数位数的字符串。parseFloat()
将其转回数字,并舍入到指定精度。
⚠️ 注意:
toFixed()
返回字符串,需用parseFloat()
或Number()
转回。
方法 4:使用专门的库
对于高精度计算,建议使用专门的库:
decimal.js
big.js
bignumber.js
// 使用 decimal.js 示例
const Decimal = require('decimal.js');
const a = new Decimal(0.1);
const b = new Decimal(0.2);
const sum = a.plus(b);
console.log(sum.equals(0.3)); // true
这些库使用十进制算术,避免了二进制浮点数的精度问题。
三、总结
问题 | 解答 |
---|---|
为什么 0.1 + 0.2 !== 0.3 ? | 因为 0.1 和 0.2 在二进制中无法精确表示,导致计算时有微小的精度损失。 |
如何解决? | 使用 Number.EPSILON 进行近似比较(✅ 推荐)转换为整数运算使用 toFixed() + parseFloat() 使用高精度计算库(如 decimal.js ) |
核心思想:JavaScript 的浮点数精度问题是语言底层机制决定的,无法完全避免。在涉及小数计算时,应避免直接使用 ===
比较,而是采用近似比较或整数化策略。
THE END