带联合的二进制表示

时间:2016-10-15 09:13:37

标签: c unions

在以下程序中:

union
{
  int i;
  float f;
} u;

假设32位编译器,u在内存中分配了4个字节。

u.f = 3.14159f;

3.14159f使用IEEE 754表示,在这4个字节中。

printf("As integer: %08x\n", u.i);

u.i代表什么? IEEE 754二进制表示是否解释为4字节signed int

2 个答案:

答案 0 :(得分:3)

i读取实现定义 blah blah blah。

还是

On" normal"

的平台

(AKA any"普通"具有最新编译器的PC)

您将获得其位模式与原始float匹配的整数,其描述为e.g. here

float bit pattern

现在,有一个符号位用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);

(请注意,这完全忽略了所有&#34;魔法&#34;模式 - 安静和信号NaNs和次正常数字浮现在脑海中)

答案 1 :(得分:0)

根据this answer,读取除最后一个之外的联合的任何元素是未定义的行为或实现定义的行为,具体取决于标准的版本。

如果要检查3.14159f的二进制表示,可以通过转换float的地址然后解除引用来实现。

#include <stdint.h>
#include <stdio.h>


int main(){
    float f = 3.14159f;
    printf("%x\n", *(uint32_t*) &f);
}

此程序的输出为40490fd0,与this page给出的结果相符。

正如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这样的小端架构上,这将产生与第一个建议相反的顺序的字节。