为什么 0.1 + 0.2 !== 0.3?如何让它“相等”?
面试速答(30 秒版 TL;DR)
- JS 的
number是 IEEE 754 双精度浮点数,很多十进制小数(比如0.1)在二进制里是无限循环小数,只能近似表示。 - 近似值参与运算会产生误差,所以
0.1 + 0.2结果是0.30000000000000004。 - “让它相等”的工程解法是:不要直接用
===比较浮点运算结果,而是用误差容忍(EPSILON)、或转成整数运算(金额用分),或用高精度小数库。
关键原因:十进制小数在二进制里不可有限表示
验证:
0.1 + 0.2; // 0.30000000000000004
0.1 + 0.2 === 0.3; // false
工程上怎么处理“相等”?
方案 1:用误差容忍(推荐用于比较)
function nearlyEqual(a, b, eps = Number.EPSILON) {
// 同时考虑绝对误差和相对误差(避免大数比较出问题)
const diff = Math.abs(a - b);
return diff <= eps * Math.max(1, Math.abs(a), Math.abs(b));
}
nearlyEqual(0.1 + 0.2, 0.3); // true
方案 2:转成整数运算(推荐用于金额/计量)
// 金额用“分”存储和计算
const cents = 10 + 20;
cents === 30; // true
方案 3:只做显示层格式化(不要当作精确计算)
(0.1 + 0.2).toFixed(1); // "0.3"
方案 4:使用高精度十进制库(需要业务强精度时)
- 用
decimal.js、big.js等做十进制精算(面试提思路即可)。
典型题 & 标准答法
Q1:那 0.1 + 0.7 === 0.8 为什么又是 true?
因为并不是所有小数都表现出明显误差,有的恰好舍入后能得到你期望的值;但这不代表它“没问题”,只是误差刚好没暴露。
Q2:Number.EPSILON 是啥?
- 它表示
1附近两个可表示浮点数之间的最小间隔(一个非常小的常量),常用来做误差比较的尺度。
易错点/坑
toFixed返回字符串;不要把“显示格式化”当成“精确计算”。- 比较浮点结果不要用
===,除非你能证明不会产生舍入误差。
速记要点(可背诵)
- 根因:IEEE 754 + 二进制无法精确表示多数十进制小数。
- 解法:EPSILON 容忍比较,或转整数算(分/毫秒),或用十进制高精度库。