JavaScript 浮点数精度丢失原理与修复方案

作者:拾光时间:2026-10-25 09:40:35浏览:395

JavaScript 中 0.1 + 0.2 不等于 0.3 是由于 IEEE 754 二进制存储导致的精度丢失。本文解析十进制转二进制的原理,并介绍使用 Decimal、Bignumber 库及整数放大法修复该问题的具体方案。

前言

在 JavaScript 中进行十进制数值运算时,常会遭遇出乎意料的计算结果,这是开发者普遍遇到的现象。

精度丢失

以 0.1 加 0.1 为例,结果为正常的 0.2;但计算 0.1 加 0.2 时,结果并非预期的 0.3,而是 0.30000000000000004。

此类现象不仅限于加法,减法运算中同样存在。

例如执行 1.2 减 1,结果返回 0.19999999999999996。

这并非 JavaScript 独有缺陷,其他编程语言同样面临此问题。

在 Python 环境下进行相同测试。

print(.1 + .2) 

输出结果同样显示为:0.30000000000000004

原因

根本原因在于计算机底层以二进制格式存储数据。

如何将整数从十进制转换为二进制

十进制整数可通过连续除以 2 转换为二进制。记录每次除法的商,持续操作直至商为零。

每次除法需记录余数,最后将余数列表反转,即可得到对应的二进制数值。

以将 29 转换为二进制为例:

29÷2=14余1

14÷2=7余0

7÷2=3余1

3÷2=1余1

1÷2=0余1

由此得出,十进制 29 对应的二进制数为 11101。

其他示例:

  • 1 对应二进制 1

  • 10 对应二进制 1010

将小数从十进制转换为二进制

十进制小数转二进制采用“乘2取整,顺序排列”法。具体步骤为:用 2 乘以十进制小数,取积的整数部分;再用 2 乘以剩余的小数部分,再次取整数部分,如此循环,直至小数部分为零(此时 0 或 1 为最后一位),或达到所需精度。

例如将 0.375 转换为二进制:

0.375*2=0.75 得到整数部分 0

0.75*2=1.5 得到整数部分 1

0.5*2=1 得到整数部分 1,小数部分归零,计算结束。最终二进制表示为 0.011

其他示例:

  • 0.1 的二进制表示为 0.0001100110011001100110011001100110011001100110011001101
  • 0.2 的二进制表示为 0.001100110011001100110011001100110011001100110011001101

并非所有十进制数都能用二进制完美表示,部分数字转换后会产生无限循环小数。

JavaScript 计算时会先将十进制数转换为二进制进行运算。

0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111

上述二进制结果 0.0100110011001100110011001100110011001100110011001100111 转换回十进制后即为 0.30000000000000004。

解决方案

第三方库

Decimal

x = new Decimal(0.1)
y = x.plus(0.2) 

bignumber

x = new BigNumber(0.1)
y = x.plus(0.2)    

变成整数

核心思路是:将小数拆分为字符串,计算小数部分的长度,利用该长度将小数放大为整数进行计算。

function add(num1, num2) {
  const num1Len = (num1.toString().split('.')[1] ).length;
  const num2Len = (num2.toString().split('.')[1] ).length;
  const maxLen = Math.pow(10, Math.max(num1Len, num2Len));
  return (num1 * maxLen + num2 * maxLen) / maxLen;
}

总结

至此,关于JavaScript中数字精度丢失问题的成因剖析与应对策略的探讨告一段落。如需获取更多有关JS数字精度丢失的相关信息,敬请查阅本站过往文章或继续阅读下文。期待大家持续支持本站!

作者最新文章