Как вывести целое число формата IEEE-754 как число с плавающей точкой - PullRequest
4 голосов
/ 06 декабря 2009

У меня есть длинное целое число без знака, которое представляет число с плавающей запятой в формате 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»)

Ответы [ 5 ]

7 голосов
/ 06 декабря 2009

Метод объединения, который вы предложили, - это обычный маршрут, по которому идет большинство людей. Тем не менее, технически неопределенное поведение в C / 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));

Хотя, честно говоря, я бы просто использовал метод объединения. Из ссылки на cellperformance.com выше:

Это очень распространенная идиома, которая хорошо поддерживается всеми основными компиляторами. На практике чтение и письмо любому члену профсоюза в любом порядке является приемлемой практикой.

3 голосов
/ 06 декабря 2009

Он будет работать только на 32-битных машинах или машинах, где sizeof (long) == sizeof (float). На современных машинах вы можете вместо этого использовать int ... и вам действительно нужно позаботиться о выполнении этого трюка.

3 голосов
/ 06 декабря 2009

Я думал о том же, что и Джон Скит, но он меня опередил. Однако я бы сделал это немного более кратко, чем он.

cout << "float value is: " << *((float *) &ulValue)) << endl;

Вы получаете указатель на ваш unsigned long, затем интерпретируете его как указатель на float, а затем разыменовываете указатель на float для получения значения float.

Поскольку вы делаете это в C ++, более понятно использовать reinterpret_cast вместо старого приведения в стиле C.

cout << "float value is: " << *(reinterpret_cast<float *>(&ulValue)) << endl;
3 голосов
/ 06 декабря 2009

РЕДАКТИРОВАТЬ: Раньше я думал, что это "просто противно", но это оказывается хуже, чем я думал - подробности смотрите в комментариях. Я бы не рекомендовал такой подход, но я оставляю его здесь, чтобы задокументировать возможность (и предостережение!)


Вы можете использовать указатели:

long ul = 1084227584;
float* fp = (float*) &ul;
float f = *fp;

(Это может быть сжато в одну строку, но я думаю, что это не так.)

В основном то же самое, что и ваш союз ... и одинаково хитро, если вы не уверены в размерах участвующих типов.

Если ваша платформа поддерживает их, вам, вероятно, следует использовать специфичные для размера имена типов для целочисленных типов и типов с плавающей запятой.

1 голос
/ 07 декабря 2009

шведы имели правильное направление, но пропустили один символ. Правильный путь

long l = 1084227584L
float f = reinterpret_cast<float&>(l);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...