在这篇文章中使用了一个我不明白的方程:
- 我 = (e + B) * L + m * L
I是解释为整数的浮点数的字节表示。这是一个例子:
float x = 3.5f;
unsigned int i = *((unsigned int *)&x);
- e是浮点数的指数。
- B是偏差 (127)。
- L是一个常数 (1 << 23)。
- m是尾数。
现在我的问题是:
为什么这个方程是正确的,我在哪里可以阅读更多关于这个方程的信息?
在这篇文章中使用了一个我不明白的方程:
I是解释为整数的浮点数的字节表示。这是一个例子:
float x = 3.5f;
unsigned int i = *((unsigned int *)&x);
现在我的问题是:
为什么这个方程是正确的,我在哪里可以阅读更多关于这个方程的信息?
如您所知,浮点数存储在 IEEE 754 标准中。单精度浮点的位模式如下(详见此处):
并且数字的值根据以下公式计算:
因此,对于 32 位值,等效整数为e * L + m
. 因为指数从(第 23 位)开始,第一部分是m
. 假设指数与 一起存储-127
,则表达式转换为(e + B)*L + m
。
关于L
之后m
可能有一个文章中可能没有提到的假设。
此外,sign
该公式中不考虑位。
浮点数用符号s、指数e和有效数f表示。(有些人使用术语“尾数”,但这是对数纸质表格时代的遗留物。浮点值的小数部分首选“有效数”。尾数是对数。有效数是线性的。)二进制浮点,表示的值是 + 2 e • f或 - 2 e • f,根据符号s。
通常对于二进制浮点,有效位需要在 [1, 2) 中,至少对于格式正常范围内的数字。对于编码,第一位与其余位分开,因此我们可以写为f = 1 + r,其中 0 ≤ r < 1。
在 IEEE 754 基本二进制格式中,浮点数被编码为一个符号位、一些指数位和一个有效位字段:
符号s用 0 位表示正数,1 表示负数。由于我们采用对数,因此该数字可能是正数,并且出于当前目的我们可能会忽略符号位。
指数位是实际指数加上一些偏差B。(对于 32 位格式,B是 127。对于 64 位,它是 1023。)
有效字段包含r的位。由于r是分数,因此有效位字段包含以二进制表示的r的位,该位从“二进制点”之后开始。例如,如果r是 5/16,则二进制为“.0101000…”,因此有效数字段包含 0101000…(对于 32 位格式,有效数字段包含 23 位。对于 64 位,52 位。)
设b为有效数字段中的位数(23 或 52)。令L为 2 b。
那么r和L的乘积r • L是一个等于有效数字域内容的整数。在我们的示例中,r是 5/16,L是 2 23 = 8,388,608,并且r • L = 2,621,440。所以有效数字包含 2,621,440,即 0x280000。
等式I = ( e + B ) • L + m • L试图捕捉到这一点。首先,符号被忽略,因为它为零。那么e + B是指数加上偏差。将其乘以L会将其左移b位,这会将其置于浮点编码的指数字段的位置。然后添加r • L添加有效位字段的值(为此,我使用r表示“剩余有效位”而不是m表示“尾数”)。
因此,将 2 e • (1+ r )编码为浮点数的位在解释为二进制整数时是 ( e + B ) • L + r • L。
有关 IEEE 754 的信息在Wikipedia和IEEE 754 标准中。一些以前的 Stack Overflow 答案在此处和此处描述了编码格式。
关于您问题中的代码:
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
. (当然,它们要求 afloat
和 anunsigned int
在您使用的 C 或 C++ 实现中具有相同的大小。)