Отрицательный NaN не является NaN? - PullRequest
28 голосов
/ 30 августа 2010

Во время написания некоторых тестовых примеров и некоторых тестов проверяется результат NaN.

Я пытался использовать std::isnan, но ошибка подтверждения:

Assertion `std::isnan(x)' failed.

После печатизначение x, оказалось, это отрицательный NaN (-nan), что полностью приемлемо в моем случае.

После попытки использовать тот факт, что NaN != NaN и с использованием assert(x == x), компиляторделает мне «одолжение» и оптимизирует утверждение.

Создание моей собственной функции isNaN также оптимизируется.

Как я могу проверить оба равенства NaN и -На?

Ответы [ 6 ]

32 голосов
/ 30 августа 2010

Это смущает.

Причина, по которой компилятор (в данном случае 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 небезопасно.

4 голосов
/ 17 сентября 2011

isnan(), как ожидается, будет иметь неопределенное поведение с -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
1 голос
/ 30 августа 2010

Это похоже на ошибку в реализации вашей библиотеки isnan() для меня. Он отлично работает здесь, на gcc 4.2.1 на Snow Leopard. Тем не менее, как насчет этого?

std::isnan(std::abs(yourNanVariable));

Очевидно, я не могу это проверить, поскольку std::isnan(-NaN) - это true в моей системе.

EDIT : С -ffast-math, независимо от переключателя -O, gcc 4.2.1 на Snow Leopard считает, что NAN == NAN равно true, как и NAN == -NAN. Это может привести к катастрофическому нарушению кода. Я бы посоветовал не использовать -ffast-math или, по крайней мере, тестировать идентичные результаты при использовании сборок, а не при их использовании ...

0 голосов
/ 30 августа 2010

Вы можете проверить биты числа.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 '.

Это может быть не переносимо, но если вы уверены в своей платформе, это может быть приемлемо.Подробнее: http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlf101l.doc/xlfopg/fpieee.htm

0 голосов
/ 30 августа 2010

Это основано на статье в википедии, размещенной в комментариях. Обратите внимание, что это полностью не проверено - оно должно дать вам представление о том, что вы можете сделать, хотя.

bool reallyIsNan(float x)
{
    //Assumes sizeof(float) == sizeof(int)
    int intIzedX = *(reinterpret_cast<int *>(&x));
    int clearAllNonNanBits = intIzedX & 0x7F800000;
    return clearAllNonNanBits == 0x7F800000;
}

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

0 голосов
/ 30 августа 2010

Есть C99 isnan (), который вы должны использовать.

Если в вашей реализации он не работает правильно (какой это?), Вы можете реализовать свою собственную, переинтерпретируя _casting для long и делаяIEEE немного магии.

...