JavaScript浮点运算不安全,应转为整数运算(如元→分),比较用容差法,高精度需求选decimal.js或big.js,避免toFixed等二次解析陷阱。

JavaScript 的算术运算符本身不提供浮点数安全保障,因为所有数字都按 IEEE 754 双精度浮点格式存储和运算。0.1 + 0.2 !== 0.3 不是 bug,而是二进制表示限制下的必然结果。安全计算的关键在于**绕过浮点路径**,而不是依赖 +、-、*、/ 直接处理小数。
用整数单位替代小数参与运算
这是最可靠、零依赖的方案,尤其适用于金额、计费等关键场景:
- 把元转为分:乘以 100 后用 Math.round() 截断浮点误差,例如
Math.round(19.99 * 100)→1999(不能用parseInt或直接取整) - 全程用整数加减乘除:如三件商品总价 =
1999 * 3→5997 - 仅在展示时还原:除以 100 得到
59.97,不用于后续计算 - 注意乘除优先级:混合运算务必加括号,如
Math.round((a + b) * 100)
比较小数时使用容差而非严格相等
永远不要写 0.1 + 0.2 === 0.3:
- 定义一个极小阈值,如
const EPSILON = 1e-10 - 用
Math.abs(a - b) 判断是否“足够接近” - 金融系统中更推荐用整数比对,例如统一转成分再比较
高精度需求选轻量第三方库
当必须保留小数位并做链式运算(如汇率换算、科学计算)时:
-
decimal.js:API 类似原生 Number,支持指定精度与四舍五入模式,
new Decimal('0.1').plus('0.2').equals('0.3')返回true - big.js:体积更小(约 6KB),适合前端嵌入,API 精简但够用
- 避免 mathjs 等重型库——默认仍走浮点路径,启用 BigNumber 模式反而增加复杂度
- 传参一律用字符串,如
new Decimal('0.1'),避免new Decimal(0.1)在构造时就失真
避开常见陷阱操作
这些看似方便的做法实际会引入新误差或掩盖问题:
-
toFixed(n)返回字符串,再转回数字(如parseFloat((0.1 + 0.2).toFixed(2)))会二次解析,不可靠 -
parseFloat、Number()对字符串转小数不解决底层精度问题 -
Math.floor、parseInt会直接截断,丢失精度且无法修复 19.99 * 100 → 1998.9999999999998 这类情况 - 不要对中间结果调用任何格式化方法后再参与下一轮计算
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











