Отрицательный NaN не является NaN?
При написании некоторых тестовых примеров, а некоторые тесты проверяют результат NaN.
Я попытался использовать std::isnan
, но утверждение не удалось:
Assertion `std::isnan(x)' failed.
После печати значения x
оказалось, что это отрицательное NaN (-nan
), что вполне приемлемо в моем случае.
Попытавшись использовать тот факт, что NaN != NaN
и используя assert(x == x)
, компилятор делает мне "пользу" и оптимизирует утверждение.
Кроме того, оптимизируется моя функция isNaN
.
Как я могу проверить как равенство NaN, так и -NaN?
Ответы
Ответ 1
Это неловко.
Причина, по которой компилятор (GCC в этом случае) оптимизировал сравнение, а isnan
вернулся false
был потому, что кто-то из моей команды включил -ffast-math
.
Из документов:
-ffast-math
Sets -fno-math-errno, -funsafe-math-optimizations,
-fno-trapping-math, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans and fcx-limited-range.
This option causes the preprocessor macro __FAST_MATH__ to be defined.
This option should never be turned on by any -O option since it can result in incorrect output for programs which depend on an exact implementation of IEEE or ISO rules/specifications for math functions.
Обратите внимание, что окончательное предложение - -ffast-math
небезопасно.
Ответ 2
Ожидается, что isnan()
будет иметь поведение undefined с -ffast-math
.
Это то, что я использую в своем тестовом наборе:
#if defined __FAST_MATH__
# undef isnan
#endif
#if !defined isnan
# define isnan isnan
# include <stdint.h>
static inline int isnan(float f)
{
union { float f; uint32_t x; } u = { f };
return (u.x << 1) > 0xff000000u;
}
#endif
Ответ 3
Это выглядит как ошибка в реализации библиотеки isnan()
для меня. Он отлично работает здесь на gcc 4.2.1 на Snow Leopard. Однако, как насчет этого?
std::isnan(std::abs(yourNanVariable));
Очевидно, я не могу его протестировать, так как std::isnan(-NaN)
есть true
в моей системе.
РЕДАКТИРОВАТЬ. С -ffast-math
, независимо от переключателя -O
, gcc 4.2.1 на Snow Leopard считает, что NAN == NAN
есть true
, как и NAN == -NAN
. Это может привести к катастрофическому повреждению кода. Я бы посоветовал отказаться от -ffast-math
или хотя бы тестировать одинаковые результаты в сборках, используя и не используя его...
Ответ 4
Здесь C99 isnan(), который вы должны использовать.
Если в вашей реализации это работает некорректно (что это такое?), вы можете реализовать свои собственные, путем reinterpret_casting дольше и делать IEEE бит магии.
Ответ 5
Вы можете проверить бит числа. IEEE 754 определил маску для NaN:
- Сигнал NaN представлен любой битовой диаграммой между X'7F80 0001 'и X'7FBF FFFF' или между X'FF80 0001 'и X'FFBF FFFF'.
- Тихий NaN представлен любой битовой комбинацией между X'7FC0 0000 'и X'7FFF FFFF' или между X'FFC0 0000 'и X'FFFF FFFF'.
Это может быть не переносимым, но если вы уверены в своем platfofm, это может быть приемлемым. Подробнее: http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlf101l.doc/xlfopg/fpieee.htm
Ответ 6
Это основано на статье в Википедии, размещенной в комментариях. Обратите внимание, что он полностью непроверен - он должен дать вам представление о том, что вы можете сделать, хотя.
bool reallyIsNan(float x)
{
//Assumes sizeof(float) == sizeof(int)
int intIzedX = *(reinterpret_cast<int *>(&x));
int clearAllNonNanBits = intIzedX & 0x7F800000;
return clearAllNonNanBits == 0x7F800000;
}
EDIT: Я действительно думаю, что вам стоит подумать о том, чтобы найти ошибку с парнями GLibc на этом.