标准答案
- JavaScript 的 Number 使用 IEEE 754 双精度浮点数。它用二进制表示数字,很多十进制小数无法被二进制精确表示。
- 0.1 和 0.2 转成二进制后都是无限循环小数,只能存近似值,所以 0.1 + 0.2 得到的是 0.30000000000000004,而不是精确的 0.3。
- 普通展示可以用格式化控制小数位,但格式化不等于计算精确。toFixed 返回字符串,不能把它当作后续精确计算的基础。
- 金额、库存、积分这类业务不要直接用浮点数累加。常见方案是转成最小单位整数计算,例如分;需要高精度小数时使用 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 能解决所有精度问题。
- 金额直接用浮点数累加。