Отобразить битовую комбинацию двойного как шестнадцатеричное? - PullRequest
0 голосов
/ 07 ноября 2018

Я пытаюсь использовать printf, чтобы напечатать двоичное представление типа double на консоли. Вот код, который у меня есть:

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

Я также попытался вместо этого изменить длинную длинную на длинную. Вот результат, который я получаю от этого в gcc linux:

Double a = 4.750000 (0x0)

Я почти уверен, так как моя система имеет младший порядок, что она захватывает только первые 32 бита, которые являются нулями. Однако я не уверен, как это исправить.

Я подумал, что это может быть потому, что long - это 64-битная версия в Linux, но после тестирования я снова получил тот же результат. Может я неправильно его печатаю? Не уверен, дайте мне знать и спасибо за ваше время.

Ответы [ 3 ]

0 голосов
/ 07 ноября 2018

В вашем коде есть две проблемы, одна из которых заключается в том, что вы используете неправильный спецификатор формата %llx - правильный спецификатор для unsigned long long . И clang, и gcc выдают предупреждение об этом, используя -Wall, see live .

Вторая проблема заключается в том, что вы нарушаете строгое правило алиасинга здесь:

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

правильный способ набрать текст (как на C, так и на C ++) - использовать memcpy ( посмотреть, как он работает ):

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

При строгой ссылке на псевдонимы, приведенной выше в C ++ 20, мы должны получить bit_cast, что упростит типизацию, например ::1010 *

b = bit_cast<unsigned long long>(a);
0 голосов
/ 07 ноября 2018

В C ++:

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

В качестве альтернативы:

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
0 голосов
/ 07 ноября 2018

В C, код как

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

недопустимо, поскольку нарушает правило, называемое строгим псевдонимом, для указателей одного типа, ссылающихся на объекты другого типа.

Однако есть исключение: указатель на символ может иметь псевдоним любого объекта

Таким образом, используя указатель на символ, вы можете сделать что-то подобное в 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;
}

Выход:

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

Два метода печатаются с разным порядком байтов.

Если вам не нравятся указатели, вы можете в качестве альтернативы использовать союз. Как:

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

Выход:

00 00 00 00 00 00 13 40 
40 13 00 00 00 00 00 00 
...