带联合的二进制表示
在以下程序中:
union { int i; float f; } u;
假设32位编译器, u
在内存中分配4个字节。
uf = 3.14159f;
3.14159f
使用IEEE 754表示,在这4个字节中。
printf("As integer: %08x\n", ui);
你在这里代表什么? IEEE 754二进制表示是否被解释为4字节signed int
?
从i
读取是实现定义的等等等等等等。
仍然。
在“正常”平台上
-
float
是IEEE-754 binary32格式 -
int
是32位2的补码 -
float
和int
的字节顺序是一样的 - 通过
union
的类型惩罚是明确定义的(C99 +)
(AKA任何具有最新编译器的“常规”PC)
你将得到其位模式与原始float
匹配的整数, 例如这里描述的
现在,有一个符号位用int
的2的补码表示法弄乱了东西,所以你可能想用unsigned
类型来做这种实验。 此外, memcpy
是一种更安全的方式来执行类型惩罚(你不会得到关于标准的脏看和讨论),所以如果你做了类似的事情:
float x = 1234.5678; uint32_t x_u; memcpy(&x_u, &x, sizeof x_u);
现在您可以轻松提取FP表示的各个部分:
int sign = x_u>>31; // 0 = positive; 1 = negative int exponent = ((x_u>>23) & 0xff; // apply -127 bias to obtain actual exponent int mantissa = x_u & ~((unsigned(-1)<<23);
(请注意,这完全忽略了所有“魔法”模式 - 安静和信号NaNs和次正常数字浮现在脑海中)
根据这个答案 ,从最后一个写入的联合的任何元素读取是未定义的行为或实现定义的行为,具体取决于标准的版本。
如果要检查3.14159f
的二进制表示,可以通过转换float
的地址然后解除引用来实现。
#include #include int main(){ float f = 3.14159f; printf("%x\n", *(uint32_t*) &f); }
该程序的输出为40490fd0
,与本页给出的结果相匹配。
正如interjay正确指出的那样,我上面介绍的技术违反了严格的别名规则。 要使上述代码正常工作,必须将标志-fno-strict-aliasing
传递给gcc
或等效标志,以根据其他编译器上的严格别名禁用优化。
查看不违反严格别名且不需要标志的字节的另一种方法是使用char *
。
unsigned char* cp = (unsigned char*) &f; printf("%02x%02x%02x%02x\n",cp[0],cp[1],cp[2],cp[3]);
请注意,在诸如x86之类的小端架构上,这将产生与第一个建议相反的顺序的字节。