Search code examples
cfloating-pointbit-manipulation

Obtaining bit representation of a float in C


I'm trying to use unions to obtain the bit representation of float values, my code is currently as follows:

union ufloat {
  float f;
  unsigned u;
};

int main( ) {       

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

However anything I try to print gets printed as 0.0000000, instead of as bits (such as 0001 0110, or something similar), what is wrong in my code?

Note that preferebly I would like to use unions to achieve this.


Solution

  • There are a large number of ways to accomplish this. Understand that what you are really trying to do is simply output the bits in memory that make up a float. Which in virtually all x86 type implementations are stored in IEEE-754 Single Precision Floating-Point Format. On x86 that is 32-bits of data. That is what allows a 'peek' at the bits while casting the float to unsigned (both are 32-bits, and bit-operations are defined for the unsigned type) For implementations other than x86, or even on x86 itself, a better choice for unsigned would be the exact length type of uint32_t provided by stdint.h. There can be no ambiguity in size that way.

    Now, the cast itself isn't technically the problem, it is the access of the value though dereferncing the different type (a.k.a type-punning) where you run afoul of the strict-aliasing rule (Section 6.5 (7) of the C11 Standard). The union of the float and uint32_t types give you a valid way of looking at the float bits through an unsigned type window. (you are looking at the same bits either way, it's just how you access them and tell the compiler how they should be interpreted)

    That said, you can glean good information from all of the answers here. You can write functions to access and store the bit representation of the float values in a string for later use, or output the bit values to the screen. As an exercise in playing with floating-point values a year or so back, I wrote a little function to output the bits in an annotated way that allowed easy identification of the sign, normalized exponent, and mantissa. You can adapt it or another of the answers routines to handle your needs. The short example is:

    #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;
    }
    

    Example Use/Output

    $ ./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                   |
    

    Look things over and let me know if you have any questions.