跳到主要内容

为什么 0.1 + 0.2 !== 0.3?如何让它“相等”?

面试速答(30 秒版 TL;DR)

  • JS 的 numberIEEE 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.jsbig.js 等做十进制精算(面试提思路即可)。

典型题 & 标准答法

Q1:那 0.1 + 0.7 === 0.8 为什么又是 true?

因为并不是所有小数都表现出明显误差,有的恰好舍入后能得到你期望的值;但这不代表它“没问题”,只是误差刚好没暴露。

Q2:Number.EPSILON 是啥?

  • 它表示 1 附近两个可表示浮点数之间的最小间隔(一个非常小的常量),常用来做误差比较的尺度。

易错点/坑

  • toFixed 返回字符串;不要把“显示格式化”当成“精确计算”。
  • 比较浮点结果不要用 ===,除非你能证明不会产生舍入误差。

速记要点(可背诵)

  • 根因:IEEE 754 + 二进制无法精确表示多数十进制小数。
  • 解法:EPSILON 容忍比较,或转整数算(分/毫秒),或用十进制高精度库。