Переинтерпретировать приведение из float в int

#c

#c

Вопрос:

 float f = 12.5;
unsigned int _f = *reinterpret_cast<int*>(amp;f);
std::cout << _f << std::endl; // result: 1095237632
  

Может кто-нибудь объяснить мне, как работает такое приведение? И что представлено _f ?

РЕДАКТИРОВАТЬ Итак, это число, которое я получил 1095237632 после преобразования в двоичный код, является 0b1000001010010000000000000000000 и это двоичное число находится 12.5 в IEEE-754. Я правильно понял?

Комментарии:

1. «что представлено _f?» Ничего. Вы получаете неопределенное поведение. Посмотрите этот термин.

2. Вас также может заинтересовать физическое представление IEEE-754, посмотрите это.

3. приведение не означало преобразования! любые данные имеют представление в памяти. приведение указывает компилятору использовать данное представление как другой тип. float и int не имеют общего представления, и приведение недопустимо

Ответ №1:

Давайте посмотрим на две функции. Один из них регулярно преобразует float в int, а другой из них переинтерпретирует его с помощью reinterpret_cast:

 int cast_to_int(float f) {
    // Rounds f to an int, rounding towards 0
    return (int)f; 
}
int reinterpret_cast_to_int(float i) {
    // Just copies the bits
    return *reinterpret_cast<int*>(amp;i); 
}
  

Так что же происходит на самом деле? Давайте посмотрим на сборку:

 cast_to_int(float):
    cvttss2si       eax, xmm0   // We cast to an int
    ret
reinterpret_cast_to_int(float):
    movd    eax, xmm0           // We directly copy the bits
    ret
  

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

 cast_to_int(0.7) -> 0
cast_to_int(1.0) -> 1
cast_to_int(1.5) -> 1
cast_to_int(2.1) -> 2
  

Во втором случае reinterpret_cast просто напрямую преобразует базовое представление битов. На самом деле это ничего не делает, и функция просто копирует входной регистр в выходной регистр.

Под капотом float s имеют совсем другое битовое представление, чем int s, и именно поэтому вы получаете странные цифры.

Ответ №2:

Никто не может объяснить (*), потому что это не работает.

Из cppreference:

В отличие от static_cast, но подобно const_cast, выражение reinterpret_cast не компилируется ни в какие инструкции процессора (за исключением случаев преобразования между целыми числами и указателями или на неясных архитектурах, где представление указателя зависит от его типа). Это чисто директива времени компиляции, которая предписывает компилятору обрабатывать выражение так, как если бы оно имело тип new_type.

С помощью reinterpret_cast можно выполнить только следующие преобразования, за исключением случаев, когда такие преобразования отбрасывают постоянство или изменчивость.

И затем следует список правил, описывающих, какие переинтерпретации приведений разрешены. Приведение типа A к совершенно не связанному типу B не входит в их число, и ваш код демонстрирует неопределенное поведение.

(*) Строго говоря, это неверно. Вы обрабатываете float как int, и если вы посмотрите на их представление на вашем оборудовании и если вы проверите выходные данные вашего компилятора, вы сможете понять, почему вы получаете значение, которое вы получаете, хотя неопределенное поведение не определено, и не стоит вводить детали, если вы не хотите писать непереносимый код.

Комментарии:

1. Для сложных типов поведение было бы неопределенным. Для примитивных типов одинакового размера есть причины делать такие вещи, и поведение четко определено.

2. @AntonF. Я не понимаю, что вы имеете в виду, даже если float и int имеют одинаковый размер, переосмысление a float* как int* было бы неправильным (ну, на самом деле float* и int* имеют одинаковый размер)

3. Итак, это число, которое я получил 1095237632 после преобразования в двоичный код, является 0b1000001010010000000000000000000 и это двоичное число находится 12.5 в IEEE-754. Я правильно понял?

4. @irezwi вы можете проверить это, например, здесь

Ответ №3:

Как говорится в комментариях ниже, использование reinterpret_cast небезопасно для несвязанных типов. Так что не используйте его таким образом.

Во-первых, следует избегать присвоения int uint.

Для систем x86 / x64 значение float обычно представляется в виде 4 байтов, например uint32_t, и они имеют в основном одинаковое выравнивание.

Многие компиляторы допускают следующее: uint32_t _f = * reinterpret_cast(amp;f);

Но это приводит к неопределенному поведению по некоторым причинам (спасибо за комментарии): — оптимизация — выравнивание — …

Вместо этого используйте memcpy.

Если выравнивание такое же, и значения хранятся в памяти, следующий эффект описывает, что происходит при использовании reinterpret_cast:

Ячейка памяти с плавающим значением в 4 байта равна amp;f . При переинтерпретации приведения в uint32_t эта память переинтерпретируется как uint32. Разыменованное значение _f содержит те же байты, что и значение с плавающей точкой f, но интерпретируется как uint32. Вы могли бы привести его обратно и получить исходное значение 12.5:

 float f = 12.5;
uint32_t _f = *reinterpret_cast<uint32_t*>(amp;f);
float _fnew = *reinterpret_cast<float*>(amp;_f);
std::cout << _fnew << std::endl; // result: 12.5
  

reinterpret_cast только переосмысливает ячейки памяти (адреса).
Если выравнивание не совпадает или значения хранятся в регистрах, оптимизированы и т. Д., Приведение может привести к неопределенным значениям.

Комментарии:

1. «float представлен в виде 4 байтов в памяти». не обязательно. И поведение вашего кода не определено.

2. «Следует избегать присваивания int» вводит в заблуждение. float x = 3.5; int y = x; все в порядке, если это то, что вы хотите сделать

3. @user463035818 Если вы хотите переосмыслить значение, а не получить приведенное значение по некоторым причинам, мой ответ кажется мне правильным и объясняет, что было задано («как работает такое приведение»).).

4. @Bathsheba В общем, вы правы. Я предположил, что системы x86 / x64 (поэтому я отредактировал свой ответ). В более общем случае вы могли бы статически утверждать размер float. Тогда поведение четко определено.

5. «Мне кажется правильным». Это явно не разрешено стандартом. Я не уверен, что еще можно сказать?