Как вывести целое число формата IEEE-754 как число с плавающей запятой
-
13-09-2019 - |
Вопрос
У меня есть длинное целое число без знака, которое представляет собой число с плавающей запятой в формате IEEE-754.Каков самый быстрый способ распечатать его как число с плавающей запятой на C++?
Я знаю один способ, но мне интересно, есть ли на C++ удобная утилита, которая была бы лучше.
Пример того, что я знаю:
union
{
unsigned long ul;
float f;
} u;
u.ul = 1084227584; // in HEX, this is 0x40A00000
cout << "float value is: " << u.f << endl;
(Это выводит «плавающее значение:5 дюймов)
Решение
Предложенный вами метод объединения — это обычный путь, по которому пойдет большинство людей.Однако технически это неопределенное поведение в C/C++ для чтения члена объединения, отличного от того, который был записан последним.Несмотря на это, он хорошо поддерживается практически всеми компиляторами.
Приведение указателей, как Джон Скит предложил, это плохая идея – это нарушает строгий псевдоним правила С.Агрессивный оптимизирующий компилятор выдаст при этом неверный код, поскольку предполагает, что указатели типов unsigned long *
и float *
никогда не будут псевдонимами друг друга.
Самый правильный способ, с точки зрения соответствия стандартам (то есть не вызывая неопределенного поведения), — это пропустить char*
, поскольку строгие правила псевдонимов допускают char*
указатель на псевдоним указателя любого другого типа:
unsigned long ul = 0x40A00000;
float f;
char *pul = (char *)&ul; // ok, char* can alias any type
char *pf = (char *)&f; // ok, char* can alias any type
memcpy(pf, pul, sizeof(float));
Хотя, честно говоря, я бы просто выбрал метод объединения.По ссылке на cell Performance.com выше:
Это чрезвычайно распространенная идиома, хорошо поддерживаемая всеми основными компиляторами.С практической точки зрения чтение и письмо любому члену профсоюза в любом порядке является приемлемой практикой.
Другие советы
Он будет работать только на 32-битных машинах или машинах, где sizeof(long) == sizeof(float).На современных машинах вместо этого вы можете использовать int...и вам действительно нужно быть осторожным, если вы выполняете этот трюк.
Я думал о том же, что и Джон Скит, но он меня опередил.Однако я бы сделал это немного более лаконично, чем он.
cout << "float value is: " << *((float *) &ulValue)) << endl;
Вы получаете указатель на свой беззнаковый длинный элемент, затем интерпретируете его как указатель на число с плавающей запятой, а затем разыменование указателя на число с плавающей запятой дает значение с плавающей запятой.
Поскольку вы делаете это на C++, проще использовать reinterpret_cast вместо старого приведения в стиле C.
cout << "float value is: " << *(reinterpret_cast<float *>(&ulValue)) << endl;
РЕДАКТИРОВАТЬ:Раньше я думал, что это «просто противно», но оказалось, что это хуже, чем я думал — подробности смотрите в комментариях.Я бы не рекомендовал этот подход, но оставляю его здесь, чтобы документировать возможность (и предостережение!)
Вы можете использовать указатели:
long ul = 1084227584;
float* fp = (float*) &ul;
float f = *fp;
(Это можно сжать в одну строку, но я думаю, что проще этого не делать.)
По сути то же самое, что и ваш профсоюз...и столь же хитроумно, если вы не уверены в размерах задействованных типов.
Если ваша платформа их поддерживает, вам, вероятно, следует использовать имена типов, зависящие от размера, как для целочисленных типов, так и для типов с плавающей запятой.
Свеги указал правильное направление, но пропустил один символ.Правильный путь
long l = 1084227584L
float f = reinterpret_cast<float&>(l);