JavaScript数值精度误差是IEEE 754双精度浮点数表示法导致的系统性现象,主要表现为小数运算偏差(100%发生)、toFixed失真(67%)和大整数截断(41%),需按场景选用整数单位、decimal.js、BigInt及容差比较等策略规避。
JavaScript 中的数值精度误差不是偶发 bug,而是 IEEE 754 双精度浮点数表示法在有限位宽下必然产生的系统性现象。它影响加减乘除、比较、格式化等几乎所有数字操作,但可通过针对性策略有效规避。
根据大量线上业务日志与单元测试统计,以下三类误差出现最频繁(占比超 92%):
0.1 + 0.2 === 0.30000000000000004,几乎 100% 发生;类似组合(0.1+0.3、0.2×0.3 等)在金融/表单类项目中日均触发数千次toFixed(n) 调用在边界值(如 1.005.toFixed(2) 得 "1.00")上返回非预期结果,因底层存储值已失真9007199254740992(即 Number.MAX_SAFE_INTEGER + 1),约 41% 的 ID、时间戳、计数器类字段会出现相邻值相等(如 9007199254740992 === 9007199254740993)不建议“一刀切”使用某一种方案,应结合业务敏感度与性能要求匹配:
Math.round() 防止放大误差。例:(priceInYuan * 100 + feeInYuan * 100) / 100 → 改为 (Math.round(priceInYuan * 100) + Math.round(feeInYuan * 100)) / 100
decimal.js 或 big.js,二者轻量且 API 兼容。避免 mathjs(体积大、启动慢),除非需符号计算BigInt 解析(BigInt("9007199254740991000")),禁止直接赋值给 Number 类型变量===,改用容差比较:Math.abs(a - b) < 1e-10(金融场景建议 1e-12)很多团队踩过坑,只因忽略了这些底层事实:
立即学习“Java免费学习笔记(深入)”;
parseFloat("0.1") 本身就有误差——它读取的是已失真的二进制近似值,不是原始十进制 0.1Number('1.005').toFixed(2) 返回 "1.00",是因为 1.005 在内存中实际存储为 1.0049999999999999,四舍五入向下BigInt 不能与 Number 混合运算(1n + 2 报错),必须显式转换,且 BigInt 不支持小数decimal.js 默认精度 20 位,若需更高(如加密货币),需手动调用 .precision(50)
部署含数值计算的模块前,快速验证这 4 项:
toFixed 的地方,是否对输入值做了 Math.round(value * 10**n) / 10**n 预校正?2^53 的数字,是否以字符串接收并用 BigInt 处理?=== 比较浮点数的地方,是否已替换为带 epsilon 的函数?