2

I am trying to use printf in order to print the binary representation of a double onto the console. Here is the code that I have so far:

#include <stdio.h>

int main()
{
    double a = 4.75;
    unsigned long long b = *(unsigned long long *) &a;
    printf("Double a = %f (0x%x)" a, b);
}

I also tried changing it from a long long to a long instead. Here is the result that I get from doing this in gcc linux:

Double a = 4.750000 (0x0)

I'm pretty sure since my system is little endian that it is only grabbing the first 32 bits which are all zeroes. However, I'm not sure how to fix this.

I thought it might have been because long is 64 bit in linux but after testing that I got the same result again. Maybe I'm printing it incorrectly? Not sure, let me know and thanks for your time.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
zicameau
  • 139
  • 1
  • 11
  • 4
    Does not `*(unsigned long long *) &a;` violate strict aliasing? – Sourav Ghosh Nov 07 '18 at 06:17
  • 1
    @SouravGhosh Yes, see [What is strict aliasing](https://stackoverflow.com/a/51228315/1708801) – Shafik Yaghmour Nov 07 '18 at 06:18
  • @ShafikYaghmour Thanks for the confirmation sir, that was more of a rhetorical q. :) – Sourav Ghosh Nov 07 '18 at 06:20
  • 1
    `printf("Double a = %f (0x%llx)", a, b);` – ReAl Nov 07 '18 at 06:21
  • 2
    @SouravGhosh the information was for the general audience who may not understand ;-) – Shafik Yaghmour Nov 07 '18 at 06:21
  • 1
    I really don't see how this is _not_ a dupe of the strict aliasing question. If we are to keep it open, the question needs to be narrowed down to C or C++. Currently it is too broad. It would be helpful if high rep users with gold badges did an effort in moderating the cross-posting aspect of C and C++ tags _before_ writing answers. – Lundin Nov 07 '18 at 15:20

3 Answers3

3

In C, code like

unsigned long long b = *(unsigned long long *) &a;

is illegal as it violates a rule called strict aliasing about pointers of one type referencing objects of a different type.

However, there is on exception: a char pointer may alias any object

So using char pointer, you can do something like this in C:

#include <stdio.h>

int main()
{
    double a = 4.75;
    unsigned char* p;

    // Method 1 - direct memory read
    p = (unsigned char*)&a;
    for (size_t i=0; i < sizeof(double); ++i)
    {
        printf("%02x ", *p);
        ++p;
    }
    printf("\n");

    // Method 2 - reversed memory read
    size_t i = sizeof(double);
    p = (unsigned char*)&a + i - 1;
    do
    {
        printf("%02x ", *p);
        --p;
        --i;
    } while(i > 0);

    return 0;
}

Output:

00 00 00 00 00 00 13 40 
40 13 00 00 00 00 00 00

The two methods prints with different endianness.

If you don't like pointers, you can as an alternative use a union. Like:

#include <stdio.h>

int main()
{
    union
    {
        double a;
        char c[sizeof(double)];
    } d;
    d.a = 4.75;

    for (size_t i=0; i < sizeof(double); ++i)
    {
        printf("%02x ", d.c[i]);
    }
    printf("\n");

    size_t i = sizeof(double);
    do
    {
        --i;
        printf("%02x ", d.c[i]);
    }
    while (i > 0);
    printf("\n");


    return 0;
}

Output:

00 00 00 00 00 00 13 40 
40 13 00 00 00 00 00 00 
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • 1
    A minor addition: The type-punning is standard conform in C11 but not allowed in C++. You dedicated your answer to C. Hopefully, OP did notice this. – Scheff's Cat Nov 07 '18 at 06:43
3

Your code has two issues, the simple one is that you are using the wrong format specifier %llx is the correct specifier for unsigned long long. Both clang and gcc provide a warning for this using -Wall, see it live.

The second issue is that you are violating the strict aliasing rule here:

unsigned long long b = *(unsigned long long *) &a;

the correct way to type-pun (in both C and C++) is to use memcpy (see it working live):

std::memcpy( &b, &a, sizeof(double));

At the strict aliasing link above notes in C++20 we should get bit_cast which will simplify type-punning e.g.:

b = bit_cast<unsigned long long>(a);
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
2

In C++:

double a = 4.75;
char tmp[sizeof(double)];
memcpy(tmp, &a, sizeof(double));
... // print individual bytes as hex

Alternatively:

double a = 4.75;
unsigned char* tmp = reinterpret_cast<unsigned char*>(&a);
for (int i = 0; i < sizeof(double); i++)
  ... // print *(tmp + i) as hex
Daniel Langr
  • 22,196
  • 3
  • 50
  • 93