Search code examples
cansi-c

Printing a binary representation of float/double gives the same result for two different numbers


I have to print a binary representation (from the memory) for float / doubles. While it's working pretty nicely for integers, I got strange behavior for some floats.

I mean, the results for 3.14 and 21.37 are the same (as double). For 8.5 I'm getting 0s.

21.37 as double:

0101000111101011100001010001111101010001111010111000010100011111

3.14 as double:

0101000111101011100001010001111101010001111010111000010100011111

8.5 as double:

0000000000000000000000000000000000000000000000000000000000000000

#include <stdio.h>
int printBit(int c, int i) {
    return (c & (1 << (i - 1))) ? 1 : 0;
}

int main()
{
    double f;
    int *b;
    scanf("%lf", &f);

    b = &f;

    int i;
    printf("Jako double: \n");
    for (i = sizeof(f) * 8; i > 0; i--)
    {
        printf("%i", printBit(*b, i));
    }

    printf("\n");
}

Solution

  • You function for sure is not printing the correct data.

    The problem is that the sizeof(int) in your system is smaller than sizeof(double). There is strict aliasing problem as well. This is actually strictly theoretical here, but generally speaking do not use pointer punning at all.

    So how to pun types:

    1. Use unions
    2. Use memcpy
    3. Use char array.

    method 1 & 3 are demonstrated below.

    Union punning (https://godbolt.org/z/qxi8sK)

    void prinFloatAsUnsigned(float x)
    {
        union
        {
            uint32_t u;
            float f;
        }u32 = {.f = x};
    
        printf("sizeof(float) = %zu flaot as unsigned = 0x%x\n", sizeof(float), u32.u);
    }
    
    int main(void)
    {
        prinFloatAsUnsigned(3.14f);
    }
    

    memcpy method

    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <stdint.h>
    
    void printasbin(void *buf)
    {
        uint64_t val;
    
        memcpy(&val, buf, sizeof(val));
        for(int x = 63; x >=0; x--)
            printf("%c", '0' + !!(val & (1ULL << x)));
        printf("\n");
    }
    
    
    int main(void)
    {
        double x = 3.14;
        printf("%f = \t",x);
        printasbin(&x);
        x = 21.37;
        printf("%f = \t",x);
        printasbin(&x);
        x = 8.5;
        printf("%f = \t",x);
        printasbin(&x);
    }
    

    result:

    3.140000  =     0100000000001001000111101011100001010001111010111000010100011111
    21.370000 =     0100000000110101010111101011100001010001111010111000010100011111
    8.500000  =     0100000000100001000000000000000000000000000000000000000000000000
    

    https://godbolt.org/z/3mLHw9

    or printed your way (a bit modified - in the IT we count bits form zero)

    int printBit(void *buff, int i) 
    {
        unsigned char *data = buff;
        return !!(data[i / 8] & (1 << (i & 7)));
    }
    
    
    int main(void)
    {
        double f = 3.14;
        printf("Jako double: %f\n", f);
        for (int i = sizeof(f) * 8 - 1; i >= 0; i--)
        {
            printf("%d", printBit(&f, i));
        }
        printf("\n");
    
        f = 21.37;
        printf("Jako double: %f\n", f);
        for (int i = sizeof(f) * 8 - 1; i >= 0; i--)
        {
            printf("%d", printBit(&f, i));
        }
        printf("\n");
    
        f = 8.5;
        printf("Jako double: %f\n", f);
        for (int i = sizeof(f) * 8 - 1; i >= 0; i--)
        {
            printf("%d", printBit(&f, i));
        }
        printf("\n");
    

    result:

    Jako double: 3.140000
    0100000000001001000111101011100001010001111010111000010100011111
    Jako double: 21.370000
    0100000000110101010111101011100001010001111010111000010100011111
    Jako double: 8.500000
    0100000000100001000000000000000000000000000000000000000000000000
    

    https://godbolt.org/z/Gn4vpK