Search code examples
cfloating-pointprintfieee-754

Representing float point in hex with printf("%a", 3.14)


A simple code like this printf("hex representation for %f is [%a]\n", 3.14, 3.14);, I expect the result to be 0x4048f5c3 (full binary version is: 0100 0000 0100 1000 1111 0101 1100 0011, 0x4.8f5c3p-1) according to reference websites below, but the compiled executable shows [0xc.8f5c3p-2] (binary: 1100.1000 1111 0101 1100 0011), why did the C compiler shows exponent as -2 instead of -1?

The compiler setting is:

"command": "c:/msys64/mingw64/bin/gcc.exe",
"args": [
            "-g",               
            "${file}",
            "-o",
            "${fileDirname}\\${fileBasenameNoExtension}.exe",
            "-Wall",
            "-Werror",
            "-std=c11"

Reference: https://users.cs.fiu.edu/~downeyt/cop2400/float.htm https://gregstoll.com/~gregstoll/floattohex/


Solution

  • The website you are using is displaying the binary representation of a floating-point number. For example, 3.14 is 40 48 F5 C3 in big-endian, or C3 F5 48 40 in little-endian.

    The hex representation in C is the actual floating-point number in base 16, not it's binary representation. 0xc.8f5c3p-2 means c.8f5c3 * 2^(-2). If we convert this to decimal using the usual conversion algorithm, we get 3.14:

    12(C) * 16^0 + 8 * 16^(-1) + 15(F) * 16^(-2) + 5 * 16^(-3) + 12(C) * 16^(-4) + 3 * 16^(-5) = 12 + 0.5 + 0.05859 + ... = 12.56 (with approximation)

    Now multiply by 2^(-2), or divide by 4, and we get 3.14.

    The difference between the 2 representations is shown here (note that this program technically causes undefined behavior in C, because of the uint32_t* to float* cast followed by dereference, but in this case the behavior is to use the binary representation to create a floating-point number, as one would expect):

    #include <stdio.h>
    #include <inttypes.h>
    
    // Assuming IEEE 754 representation of 32-bit floats
    
    int main(void)
    {
        float x = 0xC.8F5C3p-2;
        uint32_t y = 0x4048F5C3;
        printf("Base-16 representation of %f is: %A\n", x, x);
        printf("Binary representation of %f is: 0X%"PRIX32"\n", *(float*)&y, y);
    }
    

    And this is the output I get on my computer:

    Base-16 representation of 3.140000 is: 0XC.8F5C3P-2
    Binary representation of 3.140000 is: 0X4048F5C3