У меня есть 32-битное беззнаковое значение, которое я хочу преобразовать в ieee 754 с плавающей точкой.
Вот мои переменные:
unsigned int val = 0x3f800000;
float floatVal;
Я знаю, что мне нужно сделать следующее, чтобы иметь возможность правильно преобразовать значение uint в значение с плавающей запятой ieee 754, которое я ожидаю от 1.0.
floatVal = *((float*)&val);
Может ли кто-нибудь объяснить, почему floatVal = (float) val не возвращает тот же результат? Вместо 1.0 floatVal получает значение 1.0653532e + 009. Я считаю, что это частично связано с тем, что биты теряются во время этого типа приведения типов, тогда как биты сохраняются посредством приведения указателей, но было бы очень полезно более подробное объяснение.
Вкратце: float и unsigned представляют значения по-разному. Зависит от того, чего вы пытаетесь достичь. Чтобы преобразовать значение (например, чтобы 2U, преобразованный в float, имел значение 2.0f), используйте (float)val. Чтобы получить число с плавающей запятой, которое имеет такое же двоичное представление (но почти наверняка другое значение), используйте *((float *)&val) (имейте в виду, что последнее ПРЕДПОЛАГАЕТ, что float и unsigned имеют одинаковый размер и не будут работать, как вы описываете, для реализаций, использующих плавающие элементы, отличные от IEEE точка).
приведение (float) val просто игнорирует тот факт, что val закодировано в формате IEEE и, следовательно, должно быть неправильным
Чтобы объяснить, почему «получает значение 1.0653532e + 009», следует опубликовать код, ввести присвоение и распечатать его. А минимальный воспроизводимый пример





В текущих стандартах C (C99, C11) следует каламбур через объединение, а не разыменование указателя с приведением типа:
#include <stdint.h>
uint32_t float_bits(const float f)
{
union {
uint32_t u;
float f;
} temp;
temp.f = f;
return temp.u;
}
Большинство современных архитектур используют формат IEEE-754 двоичный32 для типа float, но не все. Если ваша программа предполагает это, это должно быть указано в документации.
Я лично люблю проверять это во время компиляции и терпеть неудачу при компиляции, если мой код не справляется.
В большинстве современных архитектур используется один и тот же порядок байтов (порядок байтов) для целых чисел и типов с плавающей запятой, но не для всех. Опять же, если ваша программа предполагает это, она должна указать это в документации или проверить это во время компиляции (например, используя отдельную тестовую программу в том же проекте).
В языке C выражение (type)variable преобразует значение переменной variable в тип type. Например:
int32_t my_truncate(float value)
{
return (int32_t)value;
}
Если например value == 2.125, то my_truncate(value) == 2.
Аналогично, приведение целочисленного значения к типу с плавающей запятой оценивается как значение с плавающей запятой, которое наилучшим образом представляет исходное целочисленное значение. Например, (float)425 == 425.0f. (Последний f просто сообщает, что значение относится к типу float. Если в конце значения с плавающей запятой нет f, его тип в C - double.)
«Представление хранилища» относится к тому, как значения фактически хранятся в памяти.
Разница между Кастинг и набирать текст заключается в том, что Кастинг переинтерпретирует само значение, а набирать текст переинтерпретирует то, как интерпретируется представление значения в хранилище.
Таким образом, приведение целочисленного значения к типу с плавающей запятой просто дает значение с плавающей запятой, которое наилучшим образом представляет исходное целочисленное значение; но вставка целочисленного значения (того же размера, что и float) в float означает, что представление хранилища этого целочисленного значения обрабатывается как представление хранилища значения с плавающей запятой, давая значение с плавающей запятой, которое имеет то же представление хранилища, что и исходное целочисленное значение имеет.
Аналогично в другом направлении. Преобразование типа float в беззнаковый целочисленный тип даст беззнаковое целое (и, следовательно, биты), которое имеет то же представление хранения, что и исходный float. Именно это и делает приведенный выше пример функции float_bits().
В C99 и более поздних версиях стандарта C, а также в реализациях POSIXy C вы можете использовать функцию frexpf() для разделения float на нормализованную дробь: Икс × 2n, где либо -1.0 <Икс <= -0.5f, либо 0.5 <= Икс <1; и n - целая экспонента.
При необходимости можно использовать это для построения представления ближайшего значения, которое может представлять двоичный 32, в uint32_t. Это может быть полезно на архитектурах, которые не используют двоичный код IEEE 75432 для float.
Я только что многому научился. Спасибо за информативный пост.
Отличное объяснение. Спасибо!
есть также порядок байтов. он возвращает 1.0, когда
int32-битный на машине с прямым порядком байтов. только что протестировал здесь. но вы должны использовать memcpy, чтобы избежать нарушения строгого правила псевдонима. вы должны использовать stdint & test endianness перед попыткой преобразования.