标准答案

  1. JavaScript 的 Number 使用 IEEE 754 双精度浮点数。它用二进制表示数字,很多十进制小数无法被二进制精确表示。
  2. 0.1 和 0.2 转成二进制后都是无限循环小数,只能存近似值,所以 0.1 + 0.2 得到的是 0.30000000000000004,而不是精确的 0.3。
  3. 普通展示可以用格式化控制小数位,但格式化不等于计算精确。toFixed 返回字符串,不能把它当作后续精确计算的基础。
  4. 金额、库存、积分这类业务不要直接用浮点数累加。常见方案是转成最小单位整数计算,例如分;需要高精度小数时使用 decimal 或 big number 库。普通误差比较可以用容差,但容差大小要结合数值范围设计。

题目解析

浮点误差不是 JavaScript 独有问题,而是二进制浮点表示的通用结果。能解释“十进制小数到二进制小数无法精确表示”,比只背 0.1 + 0.2 的结果更重要。

Number.EPSILON 只适合非常小范围的误差判断。数值越大,误差容忍区间通常也需要跟着业务规模调整,不能把 EPSILON 当成所有比较的通用答案。

BigInt 适合大整数,不适合小数运算,也不能和 Number 直接混算。金融小数更适合整数化或十进制计算库。

代码示例

下面示例展示浮点误差、容差比较和金额整数化计算。

JavaScript
0.1 + 0.2 // 0.30000000000000004

function nearlyEqual(a, b, tolerance = Number.EPSILON) {
  return Math.abs(a - b) < tolerance
}

nearlyEqual(0.1 + 0.2, 0.3) // true

const priceInCents = 1990
const count = 3
const totalInCents = priceInCents * count

totalInCents // 5970,展示时再格式化为 59.70

常见误区

  • 用 toFixed 后继续当成数字精确计算。toFixed 返回字符串。
  • 认为 Number.EPSILON 能解决所有精度问题。
  • 金额直接用浮点数累加。

作者信息