挣扎着联盟输出

时间:2016-10-23 13:26:46

标签: c bit-manipulation unions

我试图弄清楚这段代码大约一个小时但仍然没有运气。

#include <stdio.h>
#include <stdlib.h>

int f(float f)
{
    union un {float f; int i;} u = {f};

    return (u.i&0x7F800000) >> 23;
}

int main()
{
    printf("%d\n", f(1));

return 0;
}

我不明白这是怎么回事,我尝试了f(1),f(2),f(3),f(4),当然得到了不同的结果。我也读过很多关于工会和事情的文章。我注意到当我从返回中删除0x7F800000时,结果将是相同的。我想知道u.i是如何生成的,显然它不是一些随机垃圾,但它也不是函数参数中的一个(1)。这里发生了什么,它是如何工作的?

1 个答案:

答案 0 :(得分:2)

这实际上相当于了解浮点数如何在内存中表示。 (见IEEE 754)。

简而言之,32位浮点数将具有以下结构

  • 第31位将是总数
  • 的符号位
  • 位30 - 23将是数字的指数,偏向127
  • 位22-0将代表数字的小数部分。这是标准化的,使得十进制(实际上是二进制)点之前的数字是一。

关于联盟,回想一下联盟是一个计算机内存块,可以同时保存其中一种类型,所以声明:

   union un
   {
        float f;
        int   i;
   };

正在创建一个32位的内存块,可以在任何给定的时间保存浮点数或整数。现在,当我们使用浮点参数调用函数时,该数字的位模式将写入un的内存位置。现在,当我们使用i成员访问联合时,位模式被视为整数。

因此,32位浮点数的一般布局为seee eeee efff ffff ffff ffff ffff ffff,其中s代表符号位,e代表位和f分数位。好吧,有点胡言乱语,希望一个例子可能会有所帮助。

要将4转换为IEEE浮点,首先将7转换为二进制(我将32位数字拆分为4位半字节);

    4 = 0000 0000 0000 0000 0000 0000 0000 0111

现在我们需要将其标准化,表示这是一个提升到2的幂的数字;

    1.11 x 2^2

在这里我们需要记住,每个2的幂都将二进制点向右移动(类似于处理10的幂)。

由此,我们现在可以生成位模式

  1. 数字的整体符号为正,因此整体符号位为0.

  2. 指数为2,但我们将指数偏向127.这意味着指数-127将存储为0,而指数127将存储为255.因此,我们的指数字段将是129或1000 0001。

  3. 最后我们的标准化数字是1100 0000 0000 0000 0000 000 000.请注意,我们已经放弃了领先的“1&#39;因为它总是假设在那里。

  4. 把这一切放在一起,我们有位模式:

    4 = 0100 0000 1110 0000 0000 0000 0000 0000

  5. 现在,这里的最后一点是bit-wise和0x7F800000如果我们 以二进制写出0111 1111 1000 0000 0000 0000 0000 0000,如果我们将它与IEEE浮点数的一般布局进行比较,我们看到我们用掩码选择的是指数位,然后我们将它转​​移到剩下23位。

    所以你的程序只是打印出一个浮点数的偏差指数。例如,

        #include <stdio.h>
        #include <stdlib.h>
    
        int f(float f)
        {
             union un {float f; int i;} u = {f};
    
             return (u.i&0x7F800000) >> 23;
        }
    
        int main()
        {
             printf("%d\n", f(7));
             return 0;
        }
    

    按照我们的预期输出129