返回

强势出击,破解 JavaScript 精度谜题,探寻安全数字之旅

前端

JavaScript 精度探索之旅

0.1 + 0.2 为什么等于 0.30000000000000004?

初次接触 JavaScript 的开发者们可能对此感到困惑不解,怎么会出现这种奇怪的结果?让我们来一探究竟。

IEEE 754浮点数标准:计算机数字世界的基石

JavaScript 中的数字遵循 IEEE 754 浮点数标准,该标准定义了计算机中浮点数的表示和运算规则。浮点数由三个部分组成:符号位、指数和尾数。符号位表示数字的正负,指数表示数字的大小,尾数则表示数字的小数部分。

计算机中的数字存储

计算机中使用二进制存储数字。十进制数 0.1 在计算机中无法精确表示,因为其小数部分是无限循环的。因此,计算机会将其转换为二进制小数,即 0.000110011001100110011001100110011...。这个二进制小数是无限循环的,因此计算机无法精确存储它。

舍入误差:计算机数字处理的固有缺陷

为了解决无限循环小数的问题,计算机采用了舍入技术。舍入是指将无限循环小数截断或四舍五入为有限位数的小数。这种处理方式不可避免地会产生舍入误差。在 0.1 + 0.2 的例子中,由于舍入误差,计算机将 0.1 存储为 0.10000000000000001,将 0.2 存储为 0.20000000000000004。因此,当计算机执行 0.1 + 0.2 时,其结果为 0.30000000000000004,而不是预期的 0.3。

最大安全数:把握浮点数精度的临界点

在 JavaScript 中,存在一个称为"最大安全数"的概念。最大安全数是指在不产生舍入误差的情况下可以安全使用的最大浮点数。超过这个数值,舍入误差就会变得不可忽略。JavaScript 中的最大安全数约为 9007199254740991。如果一个数字超过了这个数值,那么在对其进行运算时就可能会产生明显的舍入误差。

实战示例:探索 JavaScript 中的舍入误差

173.8125 的二进制小数表示

为了更好地理解舍入误差是如何产生的,让我们以 173.8125 为例,将其转换为二进制小数。

首先,我们将整数部分 173 转换为二进制。我们可以通过不断除以 2 并记录余数来实现:

173 / 2 = 861
86 / 2 = 430
43 / 2 = 211
21 / 2 = 101
10 / 2 = 50
5 / 2 = 21
2 / 2 = 10
1 / 2 = 01

将余数从下往上排列,得到整数部分 173 的二进制表示为 10101001。

接下来,我们将小数部分 0.8125 转换为二进制。我们可以通过不断乘以 2 并记录整数部分来实现:

0.8125 * 2 = 1.625
0.625 * 2 = 1.25
0.25 * 2 = 0.5
0.5 * 2 = 1.0

将整数部分从上往下排列,得到小数部分 0.8125 的二进制表示为 0.1101。

因此,173.8125 的二进制小数表示为 10101001.1101。

二进制小数舍入误差的产生

由于计算机无法精确存储无限循环小数,因此在存储二进制小数 0.1101 时会产生舍入误差。计算机将该小数舍入为有限位数的小数,如 0.110011。这种舍入误差会导致在进行浮点数运算时产生误差。

结语

JavaScript 中的浮点数精度问题是由于 IEEE 754 浮点数标准和计算机存储数字的方式决定的。舍入误差是计算机数字处理的固有缺陷,而最大安全数则是把握浮点数精度临界点的重要概念。通过了解这些概念,开发者们可以在日常开发中更加有效地处理浮点数,避免不必要的精度问题。