浮点数作为整数方程的字节表示

Byte representation of float as integer equation

this文章中使用了一个等式我不明白:

I 是解释为整数的浮点数的字节表示形式。 这是一个例子:

float x = 3.5f;
unsigned int i = *((unsigned int *)&x);

现在我的问题是:

为什么方程是正确的,我在哪里可以阅读更多关于这个方程的信息?

如您所知,浮点数存储在 IEEE 754 标准中。单精度浮点数的位模式如下(详见here):

而数字的值是根据以下公式计算的:

因此,对于 32 位值,等效整数为 e * L + m。 因为指数是从(第23位)开始的,第一部分是m。 由于假设指数存储为 -127,表达式将转换为 (e + B)*L + m.

关于m之后的L可能有一个假设,文中可能没有提到。

此外,sign位不在该公式中考虑。

浮点编码

浮点数用符号 s、指数 e 和尾数 f 表示。 (有些人使用术语“尾数”,但这是纸质对数表时代遗留下来的。浮点值的小数部分首选“有效数”。尾数是对数的。有效数是线性的。)在二进制浮点数,表示的值为 + 2ef 或 − 2ef,根据符号s.

通常对于二进制浮点数,尾数必须在[1, 2),至少对于格式正常范围内的数字是这样。对于编码,第一位与其余位分开,所以我们可以写成 f = 1 + r,其中 0 ≤ r < 1.

在 IEEE 754 基本二进制格式中,浮点数被编码为一个符号位、一些指数位和一个有效数字字段:

  • 符号s编码为0位表示正,1位表示负。由于我们取的是对数,因此这个数字可能是正数,出于当前目的我们可以忽略符号位。

  • 指数位是实际指数加上一些偏差B。 (对于32位格式,B为127。对于64位,为1023。)

  • signifcand 字段包含 r 的位。由于 r 是分数,因此有效位域包含 r 的位,以二进制表示,从“二进制小数点”开始。例如r为5/16,则二进制为“.0101000…”,则有效位域为0101000…(32位格式,有效位域为23位.对于64位,52位。)

b 有效数字字段中的位数(23 或52)。设 L 为 2b.

那么rL的乘积,r L,是一个等于有效位域内容的整数。在我们的示例中,r 是 5/16,L 是 223 = 8,388,608,并且 rL = 2,621,440。所以有效数字包含 2,621,440,即 0x280000.

等式I = (e + B) • L + mL 试图捕获它。首先,符号被忽略,因为它是零。那么 e + B 就是指数加上偏差。将其乘以 L 将其左移 b 位,这将其置于浮点编码的指数字段的位置。然后添加 rL 添加有效数字字段的值(为此我使用 r 表示“其余的有效数字”而不是 m 表示“尾数”)。

因此,将 2e • (1+r) 编码为当解释为二进制整数时,浮点数是 (e + B) • L + rL.

更多信息

有关 IEEE 754 的信息在 Wikipedia and the IEEE 754 standard. Some previous Stack Overflow answers describing the encoding format at here and here

别名/重新解释位

关于您问题中的代码:

float x = 3.5f;
unsigned int i = *((unsigned int *)&x);

不要使用此代码,因为它的行为未由 C 或 C++ 标准定义。

在 C 中,使用:

#include <string.h>
...
unsigned int i; memcpy(&i, &x, sizeof i);

或:

unsigned int i = (union { float f; unsigned u; }) { x } .u;

在 C++ 中,使用:

#include <cstring>
...
unsigned int i; std::memcpy(&i, &x, sizeof i);

定义这些方法是为了将浮点编码的位重新解释为unsigned int。 (当然,他们要求 floatunsigned int 在您使用的 C 或 C++ 实现中大小相同。)