返回

揭秘 Number 类型的秘密:精度丢失之谜

前端

不老实的值:Number 类型精度丢失

引子

当我们使用 JavaScript 的 Number 类型进行小数计算时,可能会惊讶地发现结果并不总像我们预期的那样准确。例如,让我们计算一下 0.1 和 0.2 的和:

console.log(0.1 + 0.2); // 输出:0.30000000000000004

等等,为什么结果不是 0.3?这是怎么回事?

罪魁祸首:浮点数

Number 类型实际上存储的是浮点数,也称为浮点数。浮点数是一种近似值,用于表示计算机无法精确表示的实数。它们使用科学计数法(底数为 2)进行存储,类似于:

-0.12345678901234567 = -0.12345678901234567 * 2^0

问题在于,并非所有实数都可以精确地表示为浮点数。例如,十进制数 0.1 在二进制中是一个无限循环小数:

0.1 = 0.0001100110011001100110011...

由于计算机无法精确存储无限小数,因此将它四舍五入为一个近似值,即 0.10000000000000001。

加法中的精度丢失

当我们对浮点数进行加法时,计算机必须将它们对齐,使它们的指数相同。这个对齐过程可能会导致精度丢失,因为计算机可能不得不舍弃一些小数位。

例如,在 0.1 + 0.2 的计算中,计算机必须将 0.1 转换为:

0.1 = 0.0001100110011001100110011 * 2^-1

现在,我们可以对齐两个数字并进行加法:

0.0001100110011001100110011 * 2^-1 + 0.001100110011001100110011 * 2^-1 = 0.0010100110011001100110011 * 2^-1

转换回十进制,结果为:

0.0010100110011001100110011 * 2^-1 = 0.30000000000000004

正如我们所看到的,舍入和对齐的过程导致了一些精度丢失,最终导致结果不完全准确。

应对措施

为了应对 Number 类型的精度丢失问题,我们可以采取以下措施:

  • 使用固定精度计算库: 有一些库,例如 decimal.js,专为高精度计算而设计。
  • 使用 BigInt 类型: 对于整数计算,可以使用 BigInt 类型,它可以存储任意大小的整数。
  • 使用字符串或数组: 对于涉及小数的精确计算,可以使用字符串或数组来表示数字。
  • 限制小数位数: 通过使用 toFixed() 方法或类似的技术,可以将小数位数限制在一个合理的范围内。

结论

Number 类型中的精度丢失是一个常见的问题,它可能会导致小数计算不准确。通过了解这个问题的根源和采取适当的应对措施,我们可以编写出更加可靠和精确的 JavaScript 代码。