Проблемы с литьем NAN-float в int

Игнорируя, почему я хотел бы это сделать, стандарт 754 IEEE fp не определяет поведение для следующего:

float h = NAN;
printf("%x %d\n", (int)h, (int)h);

Gives: 80000000 -2147483648

В принципе, независимо от того, какое значение NAN я даю, он выводит 80000000 (hex) или -2147483648 (dec). Есть ли причина для этого и/или это правильное поведение? Если да, то как?

Я даю ему разные значения NaN: Как вручную установить значение бит для float, равное NaN?

Итак, в основном, есть случаи, когда полезная нагрузка NaN влияет на выход литья?

Спасибо!

Ответ 1

Результат приведения числа с плавающей запятой к целому числу составляет undefined для значений, не входящих в диапазон целочисленной переменной (± 1 для усечения).

A NaN выходит за пределы диапазона, поэтому результат undefined.

Фактически, в стандарте упоминается только преобразование конечных значений типов с плавающей точкой в ​​целые типы.

Ответ 2

Есть причина для такого поведения, но это не то, на что вы обычно должны положиться.

Как вы отмечаете, IEEE-754 не указывает, что происходит, когда вы конвертируете NaN с плавающей запятой в целое число, за исключением того, что он должен поднять недопустимый операционный исключение, которое, вероятно, игнорирует ваш компилятор. Компилятор C говорит, что поведение undefined, что означает, что вы не только не знаете, какой цельный результат вы получите, вы не знаете, что ваша программа будет делать вообще; стандарт позволяет программе прервать или получить сумасшедшие результаты или сделать что-либо. Вероятно, вы выполнили эту программу на процессоре Intel, и ваш компилятор, вероятно, выполнил преобразование, используя одну из встроенных инструкций. Intel очень тщательно определяет поведение команд, а поведение для преобразования NaN с плавающей запятой в 32-разрядное целое - это вернуть 0x80000000 независимо от полезной нагрузки NaN, что и было вами.

Поскольку Intel задает поведение инструкции, вы можете положиться на нее, если знаете инструкцию. Однако, поскольку компилятор не предоставляет вам таких гарантий, вы не можете полагаться на эту инструкцию.

Ответ 3

Во-первых, NAN - это все, что не считается числом с плавающей точкой в ​​соответствии со стандартом IEEE. Так что это может быть несколько вещей. В компиляторе я работаю с NAN и -NAN, поэтому это не только одно значение.

Во-вторых, каждый компилятор имеет свой набор функций isnan для проверки этого случая, поэтому программисту не нужно иметь дело с самими битами. Подводя итог, я не думаю, что заглядывание в ценность имеет значение. Вы можете просмотреть значение, чтобы увидеть его конструкцию IEEE, такую ​​как знак, мантисса и экспонента, но, опять же, каждый компилятор предоставляет свои собственные функции (или, лучше сказать, библиотеку), чтобы справиться с ним.

Однако мне нужно больше сказать о вашем тестировании.

float h = NAN;
printf("%x %d\n", (int)h, (int)h);

Кастинг, который вы делали, совершает трюк с плавающей точкой для преобразования его в int. Если вы хотите получить целое число, представленное float, выполните следующие действия

printf("%x %d\n", *(int *)&h, *(int *)&h);

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