@Anonymous12344321

Как объяснить данный фрагмент кода?

Я совсем плохо разбираюсь в c++
Есть фрагмент кода:
unsigned long long num = 0xC068C0C000000000;
cout << *(double *) &num;

num в данном случае представляет собой шестнадцатеричное число, при выводе оно переводится в десятичное число типа double.
Мне не понятно, как работает такая конструкция, т.е зачем передавать именно ссылку на переменную num?
Почему неверно работает, к примеру, такой фрагмент кода:
unsigned long long num = 0xC068C0C000000000;
 cout << (double) num;
  • Вопрос задан
  • 129 просмотров
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
В данном коде демонстрируется типичная ошибка подмены типа (type punning [?]), ведущая к неминуемому UB [?] по стандарту.

num в данном случае представляет собой шестнадцатеричное число, при выводе оно переводится в десятичное число типа double.


Вот что стандарт говорит о подмене типа.
[basic.lval#11]
If a program attempts to access the stored value of an object through a glvalue whose type is not similar to one of the following types the behavior is undefined:
(11.1) -- the dynamic type of the object,
(11.2) -- a type that is the signed or unsigned type corresponding to the dynamic type of the object, or
(11.3) -- a char, unsigned char, or std​::​byte type.

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

В итоге, правильным примером подмены типа на другой может быть только пример, где используется подмена на разрешенный тип представления объекта: char, unsigned char или std::byte для C++17.
C++ Core Guidelines в секции C.183 однозначно не советует для подмены использовать union. Этого действительно не стоит делать даже при условии того, что все современные трансляторы понимают такие конструкции так, как подозревает писатель.

Почему неверно работает, к примеру, такой фрагмент кода:

Второй блок кода означает приведение значения из типа unsigned long long в тип double. В этом случае производится именно стандартное преобразование типа, а не подмена с целью интерпретации представления памяти num иным образом.
0xC068C0C000000000 для типа unsigned long long означает 13864543383726325760, при преобразовании этого значения в тип double значение будет преобразовано в 1.3864543383726326e+19. В таком виде оно и будет выведено.

Относительно первого блока кода, лучшим примером подмены типа для кода из вопроса будет такой:
unsigned long long num = 0xC068C0C000000000;
double representation = {};
memcpy( &representation, &num, sizeof( num ) );
cout << representation;

И не стоит бояться обращения к memcpy тут. Данный код полностью соответствует стандарту и ожидаемо оптимизируется транслятором до желаемого результата.

Мне не понятно, как работает такая конструкция, т.е зачем передавать именно ссылку на переменную num?

Иногда требуется произвести приведение типа значения [?], иногда требуется произвести интерпретацию памяти этого значения другим типом. Это и называется подменой типа или алиасингом. Более детальную информацию о подмене типа, и алиасинге вообще, можно узнать из этой заметки.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@galaxy
В num хранится бинарное представление double в формате IEEE 754. Код позволяет забыть, что это int, и эти бинарные данные интерпретировать, как double.
Ваш вариант приведет просто к переводу беззнакового целого num (13864543383726325760) в double (1.3864543383726326e+19)
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы