你有没有想过计算机如何将浮点数如 3.1416 (𝝿) 或 9.109 × 10⁻³¹(电子的质量 kg)存储在有限数量的 1 和 0(即位)中?对于整数(即 17)来说,这似乎很简单。假设我们有 16 位(2 个字节)来存储数字。在 16 位中,我们可以存储 [0, 65535] 范围内的整数: (0000000000000000)₂ = (0)₁₀(0000000000010001)₂ = (1 × 2⁴) + (0 × 23) + (0 × + 2³) + (0 × + 2³) (0 × 2¹) + (1 × 2⁰) = (17)₁₀(1111111111111111)₂ = (1 × 2¹⁵) + (1 × 2¹⁴) + (1 × 2¹⁴) + (1 × 2¹²) + (1 × 2¹¹) + (1 × 2¹⁰) + (1 × 2⁹) + (1 × 2⁸) + (1 × 2⁷) + (1 × 2⁶) + (1 × 2⁵) + (1 × 2⁴) + (1 × 2³) + ( 1 × 2²) + (1 × 2¹) + (1 × 2⁰) = (65535)₁₀ 如果我们需要一个有符号整数,我们可以使用二进制补码并将 [0, 65535] 的范围向负数移动。在这种情况下,我们的 16 位将代表 [-32768, +32767] 范围内的数字。您可能已经注意到,这种方法不允许您表示像 -27.15625 这样的数字(小数点后的数字被忽略)。不过,我们并不是第一个注意到这个问题的人。大约 36 年前,一些聪明人通过引入 IEEE 754 浮点运算标准克服了这一限制。 IEEE 754 标准描述了使用这 16 位(或 32 或 64 位)存储更大范围的数字的方式(框架),包括小浮点数(小于 1 且接近于 0)。
为了了解标准背后的想法,我们可能会想起科学记数法 - 一种表达太大或太小的数字(通常会导致一长串数字)以方便地以十进制形式书写的方法。正如您从图像中看到的,数字表示可能分为三个部分: 指数 - 控制分数中小数点的移动距离和方向 我们可以省略基本部分,只需同意它的相等性到。在我们的例子中,我们将使用 2 作为基础。我们可以共享这些位并同时存储符号、指数和分数,而不是使用所有 16 位(或 32 位或 64 位)来存储数字的分数。根据我们将用于存储数字的位数,我们最终会进行以下拆分:使用这种方法,分数的位数已减少(即,对于 16 位数字,它已减少从 16 位到 10 位)。这意味着分数现在可能采用更窄的值范围(失去一些精度)。然而,由于我们还有一个指数部分,它实际上会增加最终的数字范围,并且还允许我们描述 0 和 1 之间的数字(如果指数为负)。例如,有符号 32 位整数变量的最大值为 2³¹ − 1 = 2,147,483,647,而 IEEE 754 32 位基数 2 浮点变量的最大值为 ≈ 3.4028235 × 10³⁸。
为了使负指数成为可能,IEEE 754 标准使用偏置指数。这个想法很简单 - 从指数值中减去偏差,使其为负。例如,如果指数有 5 位,则它可能采用 [0, 31] 范围内的值(此处所有值均为正)。但是如果我们从中减去 15 的值,范围将是 [-15, 16]。数字 15 被称为偏差,它是通过以下公式计算的:我试图在下图中描述将浮点数从二进制格式转换回十进制格式背后的逻辑。希望它能让您更好地了解 IEEE 754 标准的工作原理。为简单起见,这里使用 16 位数字,但同样的方法也适用于 32 位和 64 位数字。查看此图的交互式版本以打开和关闭设置位,并查看它如何影响最终结果 有关如何将位数组转换为浮点数的示例,请参阅 bitsToFloat.js(示例是有点人为,但它仍然概述了转换是如何进行的)有关如何在 JavaScript 中查看浮点数的实际二进制表示的示例,请参阅 floatAsBinaryString.js 您可能还想查看以下内容深入了解浮点数的二进制表示的资源: