Проверка, является ли double (или float) NaN в C ++ - PullRequest
349 голосов
/ 20 февраля 2009

Есть ли функция isnan ()?

PS: я в MinGW (если это имеет значение).

Я решил эту проблему с помощью isnan () из <math.h>, которого нет в <cmath>, который я #include поначалу описал.

Ответы [ 20 ]

333 голосов
/ 20 февраля 2009

В соответствии со стандартом IEEE, значения NaN имеют нечетное свойство, заключающееся в том, что при их сравнении всегда false То есть для числа с плавающей запятой f f != f будет истинным только , если f равно NaN.

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

Для любого компилятора, который утверждает, что использует IEEE с плавающей запятой, этот трюк должен работать. Но я не могу гарантировать, что это будет работать на практике. Проверьте с вашим компилятором, если сомневаетесь.

216 голосов
/ 23 января 2010

В текущей стандартной библиотеке C ++ нет функции isnan(). Он был введен в C99 и определен как макрос , а не функция. Элементы стандартной библиотеки, определенные C99, не являются частью текущего стандарта C ++ ISO / IEC 14882: 1998 и не являются его обновлением ISO / IEC 14882: 2003.

В 2005 году был предложен Технический отчет 1. TR1 обеспечивает совместимость с C99 для C ++. Несмотря на то, что он никогда официально не принимался стать стандартом C ++, многие ( GCC 4.0 + или Visual C ++ 9.0 + реализации C ++ действительно предоставляют функции TR1, все они или только некоторые (Visual C ++ 9.0 не предоставляет математических функций C99).

Если доступен TR1, то cmath включает в себя такие элементы C99, как isnan(), isfinite() и т. Д., Но они определяются как функции, а не макросы, обычно в пространстве имен std::tr1::, хотя и во многих реализациях (например, GCC 4 + в Linux или в XCode в Mac OS X 10.5+) вставьте их непосредственно в std::, чтобы std::isnan был четко определен.

Кроме того, некоторые реализации C ++ все еще делают макрос C99 isnan() доступным для C ++ (включается через cmath или math.h), что может вызвать больше недоразумений, и разработчики могут предположить, что это стандартное поведение.

Замечание о Viusal C ++, как упомянуто выше, не предоставляет std::isnan и std::tr1::isnan, но предоставляет функцию расширения, определенную как _isnan(), которая доступна с Visual C ++ 6.0

В XCode еще больше веселья. Как уже упоминалось, GCC 4+ определяет std::isnan. Для более старых версий компилятора и библиотеки XCode, кажется (здесь соответствующее обсуждение ) не было возможности проверить себя) определены две функции: __inline_isnand() для Intel и __isnand() для Power PC.

152 голосов
/ 30 мая 2013

Первое решение: если вы используете C ++ 11

С тех пор, как об этом спросили, появилось немного новых разработок: важно знать, что std::isnan() является частью C ++ 11

Синопсис

Определено в заголовке <cmath>

bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)

Определяет, является ли данный аргумент числа с плавающей точкой не-числом (NaN).

Параметры

arg: значение с плавающей запятой

Возвращаемое значение

true если аргумент NaN, false в противном случае

Ссылка

http://en.cppreference.com/w/cpp/numeric/math/isnan

Обратите внимание, что это несовместимо с -fast-math, если вы используете g ++, другие предложения приведены ниже.


Другие решения: если вы используете инструменты, не совместимые с C ++ 11

Для C99 в C это реализовано как макрос isnan(c), который возвращает значение типа int. Тип x должен быть float, double или long double.

Различные поставщики могут включать или не включать функцию isnan().

Предположительно переносимый способ проверки NaN состоит в том, чтобы использовать свойство IEEE 754, которое NaN не равно самому себе: т.е.

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

82 голосов
/ 20 февраля 2009

Существует также библиотека только для заголовков , представленная в Boost, в которой есть удобные инструменты для работы с типами данных с плавающей точкой

#include <boost/math/special_functions/fpclassify.hpp>

Вы получаете следующие функции:

template <class T> bool isfinite(T z);
template <class T> bool isinf(T t);
template <class T> bool isnan(T t);
template <class T> bool isnormal(T t);

Если у вас есть время, взгляните на весь математический инструментарий от Boost, он имеет много полезных инструментов и быстро растет.

Также при работе с плавающими и не плавающими точками было бы неплохо взглянуть на Числовые преобразования .

43 голосов
/ 26 марта 2011

Существует три "официальных" способа: posix isnan макрос , c ++ 0x isnan шаблон функции или визуальный c ++ _isnan function .

К сожалению, определить, кто из них использовать, довольно непрактично.

И, к сожалению, нет надежного способа определить, есть ли у вас представление IEEE 754 с NaN. Стандартная библиотека предлагает официальный способ (numeric_limits<double>::is_iec559). Но на практике компиляторы, такие как g ++, все испортили.

Теоретически можно было бы просто использовать x != x, но такие компиляторы, как g ++ и visual c ++ облажались.

Итак, в конце, протестируйте для конкретных NaN битовых шаблонов , предполагая (и, надеюсь, принудительно применяя, в какой-то момент!) Конкретное представление, такое как IEEE 754.


РЕДАКТИРОВАТЬ : в качестве примера "компиляторов, таких как g ++ & hellip; запутать", рассмотрим

#include <limits>
#include <assert.h>

void foo( double a, double b )
{
    assert( a != b );
}

int main()
{
    typedef std::numeric_limits<double> Info;
    double const nan1 = Info::quiet_NaN();
    double const nan2 = Info::quiet_NaN();
    foo( nan1, nan2 );
}

Компиляция с g ++ (TDM-2 mingw32) 4.4.1:

C:\test> type "C:\Program Files\@commands\gnuc.bat"
@rem -finput-charset=windows-1252
@g++ -O -pedantic -std=c++98 -Wall -Wwrite-strings %* -Wno-long-long

C:\test> gnuc x.cpp

C:\test> a && echo works... || echo !failed
works...

C:\test> gnuc x.cpp --fast-math

C:\test> a && echo works... || echo !failed
Assertion failed: a != b, file x.cpp, line 6

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
!failed

C:\test> _
38 голосов
/ 20 февраля 2009

Существует std :: isnan, если ваш компилятор поддерживает расширения c99, но я не уверен, что mingw поддерживает.

Вот небольшая функция, которая должна работать, если ваш компилятор не имеет стандартной функции:

bool custom_isnan(double var)
{
    volatile double d = var;
    return d != d;
}
25 голосов
/ 20 февраля 2009

Вы можете использовать numeric_limits<float>::quiet_NaN( ), определенный в стандартной библиотеке limits, для тестирования. Для double.

определена отдельная константа
#include <iostream>
#include <math.h>
#include <limits>

using namespace std;

int main( )
{
   cout << "The quiet NaN for type float is:  "
        << numeric_limits<float>::quiet_NaN( )
        << endl;

   float f_nan = numeric_limits<float>::quiet_NaN();

   if( isnan(f_nan) )
   {
       cout << "Float was Not a Number: " << f_nan << endl;
   }

   return 0;
}

Я не знаю, работает ли это на всех платформах, так как я тестировал только g ++ на Linux.

17 голосов
/ 20 февраля 2009

Вы можете использовать функцию isnan(), но вам необходимо включить математическую библиотеку C.

#include <cmath>

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

inline bool isnan(double x) {
    return x != x;
}
12 голосов
/ 17 августа 2013

нан профилактика

Мой ответ на этот вопрос: Не используйте ретроактивные чеки для nan. Вместо этого используйте превентивные проверки для делений вида 0.0/0.0.

#include <float.h>
float x=0.f ;             // I'm gonna divide by x!
if( !x )                  // Wait! Let me check if x is 0
  x = FLT_MIN ;           // oh, since x was 0, i'll just make it really small instead.
float y = 0.f / x ;       // whew, `nan` didn't appear.

nan результат операции 0.f/0.f или 0.0/0.0. nan - это ужасный противник стабильности вашего кода, который должен быть обнаружен и очень осторожно предотвращен 1 . Свойства nan, отличные от обычных чисел:

  • nan токсично, (5 *nan = nan)
  • nan не равно ничему, даже самому себе (nan! = nan)
  • nan не больше, чем что-либо (nan!> 0)
  • nan не меньше всего (nan! <0) </li>

Последние 2 перечисленных свойства являются нелогичными и приведут к странному поведению кода, который основан на сравнении с числом nan (3-е последнее свойство тоже странно, но вы, вероятно, никогда не увидите x != x ? в вашем коде (если вы не проверяете nan (ненадежно))).

В своем собственном коде я заметил, что значения nan имеют тенденцию приводить к трудностям поиска ошибок. (Обратите внимание, что не имеет место для inf или -inf. (-inf <0) возвращает <code>TRUE, (0 <<code>inf) возвращает ИСТИНА и даже (-inf <<code>inf) возвращает TRUE. По моему опыту, поведение кода часто по-прежнему соответствует желаемому.

что делать под nan

То, что вы хотите выполнить в 0.0/0.0 , должно рассматриваться как особый случай , но то, что вы делаете, должно зависеть от чисел, которые вы ожидаете получить из кода.

В приведенном выше примере, результат (0.f/FLT_MIN) будет 0, в основном. Вы можете захотеть, чтобы 0.0/0.0 генерировал HUGE. Таким образом,

float x=0.f, y=0.f, z;
if( !x && !y )    // 0.f/0.f case
  z = FLT_MAX ;   // biggest float possible
else
  z = y/x ;       // regular division.

Так что в приведенном выше случае, если бы x было 0.f, получилось бы inf (что имеет довольно хорошее / неразрушающее поведение, как на самом деле упоминалось выше).

Помните, целочисленное деление на 0 вызывает исключение времени выполнения . Поэтому вы всегда должны проверять целочисленное деление на 0. То, что 0.0/0.0 тихо оценивается как nan, не означает, что вы можете быть ленивым и не проверять 0.0/0.0 до того, как это произойдет.

1 Проверки для nan через x != x иногда ненадежны (x != x удаляется некоторыми оптимизирующими компиляторами, которые нарушают соответствие IEEE, особенно когда включен переключатель -ffast-math).

11 голосов
/ 05 апреля 2011

В следующем коде используется определение NAN (все установленные биты экспоненты, как минимум один набор дробных бит) и предполагается, что sizeof (int) = sizeof (float) = 4. Вы можете посмотреть NAN в Википедии для получения подробной информации.

bool IsNan( float value ) { return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000; }

...