javascript数字如何运算_如何处理数学计算和精度问题【教程】

2026-01-31 00:00:00 作者:紅蓮之龍
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"

金融/计费场景必须用整数运算

钱不能靠 parseFloattoFixed 拼凑。正确做法是把金额统一转为「最小单位」(如分),全程用整数计算:

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 只支持整数,不能直接处理小数;若必须保留小数位做精确计算(如科学计算、汇率换算),推荐轻量库:

  • 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-61e-10 更合理
  • 注意:容差比较不能替代整数化方案——它只是让判断“看起来对”,不解决底层计算偏差

真正棘手的不是“怎么算”,而是“在哪一步开始算错”。很多人在输入阶段就用 parseFloat(input.value) 把用户输入的 "19.99" 变成 19.990000000000002,后面再怎么修都晚了。

输入即整数、全程整数、输出才转小数——这条链路断掉任何一环,精度问题就会冒头。

猜你喜欢

联络方式:

400 9058 355

邮箱:8955556@qq.com

Q Q:8955556

微信二维码
在线咨询 拨打电话

电话

400 9058 355

微信二维码

微信二维码