获取C中浮点的位表示

时间:2017-06-17 21:55:18

标签: c floating-point bit-manipulation

我正在尝试使用联合来获取浮点值的位表示,我的代码目前如下:

union ufloat {
  float f;
  unsigned u;
};

int main( ) {       

   union ufloat u1;
   u1.f = 3.14159f;
   printf("u1.u : %f\n", u1.u);

然而,我尝试打印的任何内容都打印为0.0000000而不是位(例如0001 0110或类似的东西),我的代码有什么问题?

请注意,我希望使用工会来实现这一目标。

5 个答案:

答案 0 :(得分:3)

有很多方法可以实现这一目标。理解你真正想要做的只是输出构成float的内存中的位。几乎所有x86类型的实现都存储在 IEEE-754单精度浮点格式中。在x86上是32位数据。这就是在将float转换为unsigned时允许对位进行“查看”(两者都是32位,并且为unsigned类型定义了位操作)对于其他实现与x86相比,甚至在x86本身上,unsigned的更好选择是由uint32_t提供的stdint.h的确切长度类型。这种方式的大小不会有任何歧义。

现在,演员本身在技术上不是问题,它是值的访问虽然取消了你运行的不同类型(又名 type-punning )违反严格别名规则(C11标准第6.5(7)节)。 unionfloat类型的uint32_t为您提供了一种通过float类型窗口查看unsigned位的有效方式。 (无论哪种方式,你都在看同样的位,它只是你如何访问它们并告诉编译器应该如何解释它们)

那就是说,你可以从这里得到所有答案中的好信息。您可以编写函数来访问和存储字符串中float值的位表示以供以后使用,或者将位值输出到屏幕。作为一年左右玩浮点值的练习,我写了一个小函数来以带注释的方式输出位,以便轻松识别符号,归一化指数和尾数。您可以调整它或其他答案例程来满足您的需求。简短的例子是:

#include <stdio.h>
#include <stdint.h>
#include <limits.h> /* for CHAR_BIT */

/** formatted output of ieee-754 representation of float */
void show_ieee754 (float f)
{
    union {
        float f;
        uint32_t u;
    } fu = { .f = f };
    int i = sizeof f * CHAR_BIT;

    printf ("  ");
    while (i--)
        printf ("%d ", (fu.u >> i) & 0x1);

    putchar ('\n');
    printf (" |- - - - - - - - - - - - - - - - - - - - - - "
            "- - - - - - - - - -|\n");
    printf (" |s|      exp      |                  mantissa"
            "                   |\n\n");
}

int main (void) {

    float f = 3.14159f;

    printf ("\nIEEE-754 Single-Precision representation of: %f\n\n", f);
    show_ieee754 (f);

    return 0;
}

示例使用/输出

$ ./bin/floatbits

IEEE-754 Single-Precision representation of: 3.141590

  0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 0 0 1 1 1 1 1 1 0 1 0 0 0 0
 |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
 |s|      exp      |                  mantissa                   |

仔细看看,如果您有任何问题,请告诉我。

答案 1 :(得分:1)

二进制输出没有格式说明符;通常十六进制(基数16)用于方便,因为单个十六进制数字恰好代表4个二进制数字。有十六进制格式说明符(%Xprintf( "u1.u : %4X\n", u1.u ) ; )。

itoa()

或者,您可以使用#include <limits.h> #include <stdlib.h> #include <stdio.h> ... char b[sizeof(float) * CHAR_BIT + 1] = "" ; printf( "u1.u : %s\n", itoa( u1.u, b, 2 ) ) ; 生成二进制字符串表示形式(非标准但通常实现的功能)。

#define BITS (sizeof(float) * CHAR_BIT + 1) ; 
char b[BITS] = itoa( u1.u, b, 2 ) ;
printf( "u1.u : " ) ;
for( int i = 0; i < BITS - strlen(b); i++ )
{
    putchar( '0' ) ;
} 
printf( "%s\n", b ) ; 

这个问题在于它不包括前导零,并且在二进制浮点表示中所有位都很重要。有可能处理这个问题,但有些麻烦:

unsigned

请注意,在上面的示例中,与原始问题中的隐式假设相同,float至少与float一样大,并使用相同的字节顺序(较旧的ARM设备)例如,使用“跨端”浮点格式!)。在这方面我没有尝试过携带。最终,如果您只想检查print()的内存布局,那么在调试器中进行检查可能是最简单且最依赖于编译器实现的方法。

答案 2 :(得分:1)

您可以编写一个简单的print_bits - 函数并使用无符号字符数组来读出浮点数的“原始内存表示”:

void print_bits(unsigned char x)
{
    int i;
    for (i = 8 * sizeof(x) - 1; i >= 0; i--) {
        (x & (1 << i)) ? putchar('1') : putchar('0');
    }
}

typedef float ftype;

union ufloat {
    ftype f;
    unsigned char bytes[sizeof(ftype)];
};

int main( ) {
    union ufloat u1;
    u1.f = .1234;

    for (int i=0; i<sizeof(ftype); i++) {
        unsigned char b = u1.bytes[i];
        print_bits(b);putchar('-');
    }
    return 0;
}

不确定union是否真的需要(我想你是因为对齐问题和UB而引入的);使用unsigned char数组时,对齐不应成为问题。

答案 3 :(得分:0)

要将任何变量/对象转换为编码二进制文件的字符串,请参阅how to print memory bits in c

  

打印...作为位(例如0001 0110,或类似的东西),

类似的东西:使用"%a"打印float,转换为double,以十六进制显示其有效值,以及十进制幂为2的指数。@Jonathan Leffler

printf("%a\n", 3.14159f);
// sample output
0x1.921fap+1

答案 4 :(得分:0)

#include <stdio.h>

union
{
    float f;
    unsigned int u;
} myun;

int main ( void )
{
    unsigned int ra;

    printf("%p %lu\n",&myun.f,sizeof(myun.f));
    printf("%p %lu\n",&myun.u,sizeof(myun.u));
    myun.f=3.14159F;
    printf("0x%08X\n",myun.u);
    for(ra=0x80000000;ra;ra>>=1)
    {
        if(ra&myun.u) printf("1"); else printf("0");
    }
    printf("\n");

    for(ra=0x80000000;ra;ra>>=1)
    {
        if(ra==0x40000000) printf(" ");
        if(ra==0x00400000) printf(" ");
        if(ra&myun.u) printf("1"); else printf("0");
    }
    printf("\n");

    return(0);
}

0x601044 4
0x601044 4
0x40490FD0
01000000010010010000111111010000
0 10000000 10010010000111111010000