JavaScript数字运算基于IEEE 754双精度浮点数,导致0.1+0.2≠0.3是设计使然;十进制小数在二进制中无限循环,截断引发舍入误差;金融场景须转为整数(如分)计算,用Math.round()避免精度丢失;高精度需求推荐decimal.js或big.js,比较小数应使用容差而非===。

JavaScript 中的数字运算不是“直接算就完事”,而是默认用 IEEE 754 双精度浮点数表示,这意味着 0.1 + 0.2 !== 0.3 是常态,不是 bug —— 是设计如此。
为什么 0.1 + 0.2 得到 0.30000000000000004
因为十进制小数(如 0.1)在二进制中是无限循环小数,就像 1/3 在十进制中是 0.333… 一样。JS 的 Number 类型只能存有限位,截断后产生舍入误差。
- 所有涉及小数的加减乘除都可能累积这类误差,不只是
+ -
parseInt("0.1")或Math.floor(0.1)不会修复精度问题,它们只是丢弃小数部分 -
toFixed()返回字符串,且四舍五入逻辑受浏览器实现影响(比如(0.615).toFixed(2)在某些环境下返回"0.61"而非"0.62")
金融/计费场景必须用整数运算
钱不能靠 parseFloat 和 toFixed 拼凑。正确做法是把金额统一转为「最小单位」(如分),全程用整数计算:
const priceInYuan = 19.99; const priceInFen = Math.round(priceInYuan * 100); // → 1999(整数) const totalFen = priceInFen * 3; // → 5997 const totalYuan = totalFen / 100; // → 59.97(仅用于显示)
- 务必用
Math.round(),而不是Math.floor()或直接乘——避免19.99 * 100算出1998.9999999999998 - 乘除法优先级高,但混合运算时仍建议加括号明确意图,例如
Math.round((a + b) * 100) - 不要对中间结果调用
toFixed()再转回数字——它会再次引入字符串解析误差
需要高精度小数时用 BigInt 或第三方库
原生 JS 没有 decimal 类型。BigInt 只支持整数,不能直接处理小数;若必须保留小数位做精确计算(如科学计算、汇率换算),推荐轻量库:
立即学习“Java免费学习笔记(深入)”;
-
decimal.js:API 类似Number,支持指定精度、四舍五入模式,new Decimal('0.1').plus('0.2').equals('0.3')返回true -
big.js:更小(~6KB),适合前端嵌入,但 API 更精简,不支持幂运算等高级操作 - 避免用
mathjs这类重型库——它功能全但体积大,且默认仍走浮点路径,需显式启用BigNumber模式
比较两个小数是否相等?别用 ===
直接用 === 判断 0.1 + 0.2 === 0.3 必然失败。稳妥做法是设定容差(epsilon):
function equal(a, b, epsilon = Number.EPSILON) {
return Math.abs(a - b) < epsilon;
}
equal(0.1 + 0.2, 0.3); // → true-
Number.EPSILON(≈2.22e-16)适合比较接近 0 的数;对较大数值(如 >1000),应按比例放大 epsilon,例如Math.abs(a - b) - 如果是业务逻辑中的「是否足够接近」(如动画帧值判断),容差常设为
1e-6或1e-10更合理 - 注意:容差比较不能替代整数化方案——它只是让判断“看起来对”,不解决底层计算偏差
真正棘手的不是“怎么算”,而是“在哪一步开始算错”。很多人在输入阶段就用 parseFloat(input.value) 把用户输入的 "19.99" 变成 19.990000000000002,后面再怎么修都晚了。输入即整数、全程整数、输出才转小数——这条链路断掉任何一环,精度问题就会冒头。











