Проверка, является ли double (или float) NaN в C++

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

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

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

20 ответов


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

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

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


нет isnan() функция доступна в текущей стандартной библиотеке C++. Он был введен в 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(), etc. но они определяются как функции, а не макросы, обычно в 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() на ПК силы.


первое решение: если вы используете 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)

определяет, если заданное число с плавающей запятой arg не является числом (NaN).

параметры

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

вернуться значение

true если arg NaN, false иначе

ссылка

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

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


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

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

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

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

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


также библиотека только для заголовков присутствует в 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, он имеет много полезных инструментов и быстро растет.

также при работе с плавающими и неплавающими точками может быть хорошей идеей посмотреть на числовой Конверсии.


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

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

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

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

так, в конце концов, тест для конкретного NaN bitpatterns, предполагая (и, надеюсь, принуждая, в какой-то момент!) конкретное представление, такое как IEEE 754.


редактировать: в качестве примера "компиляторов, таких как g++ ... вверните это", рассмотрим

#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> _

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

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

bool custom_isnan(double var)
{
    volatile double d = var;
    return d != d;
}

можно использовать 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.


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

#include <cmath>

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

inline bool isnan(double x) {
    return x != x;
}

профилактика НАНА

мой ответ на этот вопрос не используйте обратную проверки 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 !

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

в моем собственном коде я заметил, что nan значения, как правило, трудно найти ошибки. (Обратите внимание, как это не в случае inf или -inf. (-inf TRUE, ( 0 inf) возвращает TRUE и даже (-inf inf) возвращает TRUE. Итак, по моему опыту, поведение кода часто еще как хотелось).

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

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

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

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.

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

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

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


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

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


inline bool IsNan(float f)
{
    const uint32 u = *(uint32*)&f;
    return (u&0x7F800000) == 0x7F800000 && (u&0x7FFFFF);    // Both NaN and qNan.
}

inline bool IsNan(double d)
{
    const uint64 u = *(uint64*)&d;
    return (u&0x7FF0000000000000ULL) == 0x7FF0000000000000ULL && (u&0xFFFFFFFFFFFFFULL);
}

это работает если sizeof(int) 4 и sizeof(long long) в 8.

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


начиная с C++14 существует несколько способов проверить, является ли число с плавающей запятой value - это Нэн.

из этих способов, только проверка битов записи числа , работает надежно, как отмечено в моем первоначальном ответе. В частности, std::isnan и часто предлагаемую проверку v != v, не работают надежно и не должны использоваться, чтобы ваш код перестает работать правильно, когда кто-то решает, что оптимизация с плавающей запятой необходима, и просит компилятор сделать это. Эта ситуация может измениться, компиляторы могут получить больше соответствия, но для этой проблемы, которая не произошла за 6 лет с момента первоначального ответа.

около 6 лет моим первоначальным ответом было выбранное решение для этого вопроса, которое было в порядке. Но в последнее время очень популярный ответ, рекомендующий ненадежный v != v выбран тест. Следовательно, этот дополнительный более актуальный ответ (теперь у нас есть стандарты C++11 и C++14, А C++17 на горизонт.)


основными способами проверки NaN-ness, начиная с C++14, являются:

  • std::isnan(value) )
    является предполагаемым стандартным способом библиотеки с C++11. isnan видимо конфликты с Макрос в POSIX, но на практике это не проблема. Главная проблема что когда запрашивается арифметическая оптимизация с плавающей запятой, то хотя бы с одним основным компилятором, а именно g++, std::isnan возвращает false для NaN аргумент.

  • (fpclassify(value) == FP_NAN) )
    Страдает от той же проблемы, что и std::isnan, т. е. не надежный.

  • (value != value) )
    Рекомендуется во многих ответах SO. Страдает от той же проблемы, что и std::isnan, т. е., не надежный.

  • (value == Fp_info::quiet_NaN()) )
    Это тест, который со стандартным поведением не должен обнаруживать NaNs, но это с оптимизированное поведение может обнаружить NaNs (из-за оптимизированного кода просто сравнивая bitlevel представления непосредственно), и, возможно, в сочетании с другим способом покройте стандартное не-оптимизированное поведение, смогл надежно обнаружить НАН. К сожалению оказалось, что он не работает надежно.

  • (ilogb(value) == FP_ILOGBNAN) )
    Страдает от той же проблемы, что и std::isnan, т. е. не надежный.

  • isunordered(1.2345, value) )
    Страдает от той же проблемы, что и std::isnan, т. е. не надежный.

  • is_ieee754_nan( value ) )
    Это не стандартная функция. Это проверка битов согласно IEEE 754 норматив. Это совершенно надежно но код несколько зависит от системы.


в следующем полном тестовом коде "успех" -это то, сообщает ли выражение Nan-ness значения. Для большинства выражений эта мера успеха, цель обнаружения NaNs и только NaNs, соответствует их стандартная семантика. Для (value == Fp_info::quiet_NaN()) ) выражение, однако, стандартное поведение заключается в том, что он не работает как Нан-детектор.

#include <cmath>        // std::isnan, std::fpclassify
#include <iostream>
#include <iomanip>      // std::setw
#include <limits>
#include <limits.h>     // CHAR_BIT
#include <sstream>
#include <stdint.h>     // uint64_t
using namespace std;

#define TEST( x, expr, expected ) \
    [&](){ \
        const auto value = x; \
        const bool result = expr; \
        ostringstream stream; \
        stream << boolalpha << #x " = " << x << ", (" #expr ") = " << result; \
        cout \
            << setw( 60 ) << stream.str() << "  " \
            << (result == expected? "Success" : "FAILED") \
            << endl; \
    }()

#define TEST_ALL_VARIABLES( expression ) \
    TEST( v, expression, true ); \
    TEST( u, expression, false ); \
    TEST( w, expression, false )

using Fp_info = numeric_limits<double>;

inline auto is_ieee754_nan( double const x )
    -> bool
{
    static constexpr bool   is_claimed_ieee754  = Fp_info::is_iec559;
    static constexpr int    n_bits_per_byte     = CHAR_BIT;
    using Byte = unsigned char;

    static_assert( is_claimed_ieee754, "!" );
    static_assert( n_bits_per_byte == 8, "!" );
    static_assert( sizeof( x ) == sizeof( uint64_t ), "!" );

    #ifdef _MSC_VER
        uint64_t const bits = reinterpret_cast<uint64_t const&>( x );
    #else
        Byte bytes[sizeof(x)];
        memcpy( bytes, &x, sizeof( x ) );
        uint64_t int_value;
        memcpy( &int_value, bytes, sizeof( x ) );
        uint64_t const& bits = int_value;
    #endif

    static constexpr uint64_t   sign_mask       = 0x8000000000000000;
    static constexpr uint64_t   exp_mask        = 0x7FF0000000000000;
    static constexpr uint64_t   mantissa_mask   = 0x000FFFFFFFFFFFFF;

    (void) sign_mask;
    return (bits & exp_mask) == exp_mask and (bits & mantissa_mask) != 0;
}

auto main()
    -> int
{
    double const v = Fp_info::quiet_NaN();
    double const u = 3.14;
    double const w = Fp_info::infinity();

    cout << boolalpha << left;
    cout << "Compiler claims IEEE 754 = " << Fp_info::is_iec559 << endl;
    cout << endl;;
    TEST_ALL_VARIABLES( std::isnan(value) );                    cout << endl;
    TEST_ALL_VARIABLES( (fpclassify(value) == FP_NAN) );        cout << endl;
    TEST_ALL_VARIABLES( (value != value) );                     cout << endl;
    TEST_ALL_VARIABLES( (value == Fp_info::quiet_NaN()) );      cout << endl;
    TEST_ALL_VARIABLES( (ilogb(value) == FP_ILOGBNAN) );        cout << endl;
    TEST_ALL_VARIABLES( isunordered(1.2345, value) );           cout << endl;
    TEST_ALL_VARIABLES( is_ieee754_nan( value ) );
}

результаты с g++ (обратите внимание еще раз, что стандартное поведение (value == Fp_info::quiet_NaN()) это то, что он не работает как NaN-детектор, это просто очень практический интерес здесь):

[C:\my\forums\so2  (detect NaN)]
> g++ --version | find "++"
g++ (x86_64-win32-sjlj-rev1, Built by MinGW-W64 project) 6.3.0

[C:\my\forums\so2  (detect NaN)]
> g++ foo.cpp && a
Compiler claims IEEE 754 = true

v = nan, (std::isnan(value)) = true                           Success
u = 3.14, (std::isnan(value)) = false                         Success
w = inf, (std::isnan(value)) = false                          Success

v = nan, ((fpclassify(value) == 0x0100)) = true               Success
u = 3.14, ((fpclassify(value) == 0x0100)) = false             Success
w = inf, ((fpclassify(value) == 0x0100)) = false              Success

v = nan, ((value != value)) = true                            Success
u = 3.14, ((value != value)) = false                          Success
w = inf, ((value != value)) = false                           Success

v = nan, ((value == Fp_info::quiet_NaN())) = false            FAILED
u = 3.14, ((value == Fp_info::quiet_NaN())) = false           Success
w = inf, ((value == Fp_info::quiet_NaN())) = false            Success

v = nan, ((ilogb(value) == ((int)0x80000000))) = true         Success
u = 3.14, ((ilogb(value) == ((int)0x80000000))) = false       Success
w = inf, ((ilogb(value) == ((int)0x80000000))) = false        Success

v = nan, (isunordered(1.2345, value)) = true                  Success
u = 3.14, (isunordered(1.2345, value)) = false                Success
w = inf, (isunordered(1.2345, value)) = false                 Success

v = nan, (is_ieee754_nan( value )) = true                     Success
u = 3.14, (is_ieee754_nan( value )) = false                   Success
w = inf, (is_ieee754_nan( value )) = false                    Success

[C:\my\forums\so2  (detect NaN)]
> g++ foo.cpp -ffast-math && a
Compiler claims IEEE 754 = true

v = nan, (std::isnan(value)) = false                          FAILED
u = 3.14, (std::isnan(value)) = false                         Success
w = inf, (std::isnan(value)) = false                          Success

v = nan, ((fpclassify(value) == 0x0100)) = false              FAILED
u = 3.14, ((fpclassify(value) == 0x0100)) = false             Success
w = inf, ((fpclassify(value) == 0x0100)) = false              Success

v = nan, ((value != value)) = false                           FAILED
u = 3.14, ((value != value)) = false                          Success
w = inf, ((value != value)) = false                           Success

v = nan, ((value == Fp_info::quiet_NaN())) = true             Success
u = 3.14, ((value == Fp_info::quiet_NaN())) = true            FAILED
w = inf, ((value == Fp_info::quiet_NaN())) = true             FAILED

v = nan, ((ilogb(value) == ((int)0x80000000))) = true         Success
u = 3.14, ((ilogb(value) == ((int)0x80000000))) = false       Success
w = inf, ((ilogb(value) == ((int)0x80000000))) = false        Success

v = nan, (isunordered(1.2345, value)) = false                 FAILED
u = 3.14, (isunordered(1.2345, value)) = false                Success
w = inf, (isunordered(1.2345, value)) = false                 Success

v = nan, (is_ieee754_nan( value )) = true                     Success
u = 3.14, (is_ieee754_nan( value )) = false                   Success
w = inf, (is_ieee754_nan( value )) = false                    Success

[C:\my\forums\so2  (detect NaN)]
> _

результаты с Visual C++:

[C:\my\forums\so2  (detect NaN)]
> cl /nologo- 2>&1 | find "++"
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23725 for x86

[C:\my\forums\so2  (detect NaN)]
> cl foo.cpp /Feb && b
foo.cpp
Compiler claims IEEE 754 = true

v = nan, (std::isnan(value)) = true                           Success
u = 3.14, (std::isnan(value)) = false                         Success
w = inf, (std::isnan(value)) = false                          Success

v = nan, ((fpclassify(value) == 2)) = true                    Success
u = 3.14, ((fpclassify(value) == 2)) = false                  Success
w = inf, ((fpclassify(value) == 2)) = false                   Success

v = nan, ((value != value)) = true                            Success
u = 3.14, ((value != value)) = false                          Success
w = inf, ((value != value)) = false                           Success

v = nan, ((value == Fp_info::quiet_NaN())) = false            FAILED
u = 3.14, ((value == Fp_info::quiet_NaN())) = false           Success
w = inf, ((value == Fp_info::quiet_NaN())) = false            Success

v = nan, ((ilogb(value) == 0x7fffffff)) = true                Success
u = 3.14, ((ilogb(value) == 0x7fffffff)) = false              Success
w = inf, ((ilogb(value) == 0x7fffffff)) = true                FAILED

v = nan, (isunordered(1.2345, value)) = true                  Success
u = 3.14, (isunordered(1.2345, value)) = false                Success
w = inf, (isunordered(1.2345, value)) = false                 Success

v = nan, (is_ieee754_nan( value )) = true                     Success
u = 3.14, (is_ieee754_nan( value )) = false                   Success
w = inf, (is_ieee754_nan( value )) = false                    Success

[C:\my\forums\so2  (detect NaN)]
> cl foo.cpp /Feb /fp:fast && b
foo.cpp
Compiler claims IEEE 754 = true

v = nan, (std::isnan(value)) = true                           Success
u = 3.14, (std::isnan(value)) = false                         Success
w = inf, (std::isnan(value)) = false                          Success

v = nan, ((fpclassify(value) == 2)) = true                    Success
u = 3.14, ((fpclassify(value) == 2)) = false                  Success
w = inf, ((fpclassify(value) == 2)) = false                   Success

v = nan, ((value != value)) = true                            Success
u = 3.14, ((value != value)) = false                          Success
w = inf, ((value != value)) = false                           Success

v = nan, ((value == Fp_info::quiet_NaN())) = false            FAILED
u = 3.14, ((value == Fp_info::quiet_NaN())) = false           Success
w = inf, ((value == Fp_info::quiet_NaN())) = false            Success

v = nan, ((ilogb(value) == 0x7fffffff)) = true                Success
u = 3.14, ((ilogb(value) == 0x7fffffff)) = false              Success
w = inf, ((ilogb(value) == 0x7fffffff)) = true                FAILED

v = nan, (isunordered(1.2345, value)) = true                  Success
u = 3.14, (isunordered(1.2345, value)) = false                Success
w = inf, (isunordered(1.2345, value)) = false                 Success

v = nan, (is_ieee754_nan( value )) = true                     Success
u = 3.14, (is_ieee754_nan( value )) = false                   Success
w = inf, (is_ieee754_nan( value )) = false                    Success

[C:\my\forums\so2  (detect NaN)]
> _

суммируя вышеуказанные результаты, только прямое тестирование представления битового уровня, используя is_ieee754_nan функция, определенная в этой тестовой программе, надежно работала во всех случаях как с g++, так и с Visual C++.


дополнение:
После публикации выше я узнал еще об одном возможном тестировании для NaN, упомянутом в еще один ответ, а именно ((value < 0) == (value >= 0)). Это оказалось хорошо работать с Visual C++, но не удалось с G++'S . Надежно работает только прямое тестирование bitpattern.


возможным решением, которое не зависело бы от конкретного представления IEEE для NaN, было бы следующее:

template<class T>
bool isnan( T f ) {
    T _nan =  (T)0.0/(T)0.0;
    return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}

Что касается меня, решение может быть макросом, чтобы сделать его явно встроенным и, следовательно, достаточно быстрым. Он также работает для любого типа float. Он основан на том факте, что единственный случай, когда значение не равно самому себе, - это когда значение не является числом.

#ifndef isnan
  #define isnan(a) (a != a)
#endif

после прочтения других ответов я хотел что-то, что пройдет через предупреждение сравнения с плавающей запятой и не сломается при быстрой математике. Кажется, что работает следующий код:

/*
  Portable warning-free NaN test:
    * Does not emit warning with -Wfloat-equal (does not use float comparisons)
    * Works with -O3 -ffast-math (floating-point optimization)
    * Only call to standard library is memset and memcmp via <cstring>
    * Works for IEEE 754 compliant floating-point representations
    * Also works for extended precision long double
*/

#include <cstring>
template <class T> bool isNaN(T x)
{
  /*Initialize all bits including those used for alignment to zero. This sets
  all the values to positive zero but does not clue fast math optimizations as
  to the value of the variables.*/
  T z[4];
  memset(z, 0, sizeof(z));
  z[1] = -z[0];
  z[2] = x;
  z[3] = z[0] / z[2];

  /*Rationale for following test:
    * x is 0 or -0                                --> z[2] = 0, z[3] = NaN
    * x is a negative or positive number          --> z[3] = 0
    * x is a negative or positive denormal number --> z[3] = 0
    * x is negative or positive infinity          --> z[3] = 0
      (IEEE 754 guarantees that 0 / inf is zero)
    * x is a NaN                                  --> z[3] = NaN != 0.
  */

  //Do a bitwise comparison test for positive and negative zero.
  bool z2IsZero = memcmp(&z[2], &z[0], sizeof(T)) == 0 ||
                  memcmp(&z[2], &z[1], sizeof(T)) == 0;

  bool z3IsZero = memcmp(&z[3], &z[0], sizeof(T)) == 0 ||
                  memcmp(&z[3], &z[1], sizeof(T)) == 0; 

  //If the input is bitwise zero or negative zero, then it is not NaN.
  return !z2IsZero && !z3IsZero;
}

//NaN test suite
#include <iostream>

/*If printNaN is true then only expressions that are detected as NaN print and
vice versa.*/
template <class T> void test(bool printNaN)
{
  T v[10] = {-0.0, 0.0, -1.0, 1.0,
    std::numeric_limits<T>::infinity(),
    -std::numeric_limits<T>::infinity(),
    std::numeric_limits<T>::denorm_min(),
    -std::numeric_limits<T>::denorm_min(),
    std::numeric_limits<T>::quiet_NaN(),
    std::numeric_limits<T>::signaling_NaN()};
  for(int i = 0; i < 10; i++)
  {
    for(int j = 0; j < 10; j++)
    {
      if(isNaN(v[i] + v[j]) == printNaN)
        std::cout << v[i] << "+" << v[j] << " = " << v[i] + v[j] << std::endl;
      if(isNaN(v[i] - v[j]) == printNaN)
        std::cout << v[i] << "-" << v[j] << " = " << v[i] - v[j] << std::endl;
      if(isNaN(v[i] * v[j]) == printNaN)
        std::cout << v[i] << "*" << v[j] << " = " << v[i] * v[j] << std::endl;
      if(isNaN(v[i] / v[j]) == printNaN)
        std::cout << v[i] << "/" << v[j] << " = " << v[i] / v[j] << std::endl;
    }
  }
}

//Test each floating-point type.
int main()
{
  std::cout << "NaNs:" << std::endl;
  test<float>(true);
  test<double>(true);
  test<long double>(true);
  std::cout << std::endl << "Not NaNs:" << std::endl;
  test<float>(false);
  test<double>(false);
  test<long double>(false);
  return 0;
}

учитывая это (x != x) не всегда гарантируется для NaN (например, при использовании опции-ffast-math), я использую:

#define IS_NAN(x) (((x) < 0) == ((x) >= 0))

числа не могут быть одновременно = 0, поэтому на самом деле эта проверка проходит только в том случае, если число не меньше, не больше или равно нулю. Что, по сути, вообще не число, или NaN.

вы также можете использовать это, если вы предпочитаете:

#define IS_NAN(x) (!((x)<0) && !((x)>=0)

Я не уверен, как это влияет на-ffast-math, поэтому ваш пробег может варьироваться.


это работает:

#include <iostream>
#include <math.h>
using namespace std;

int main ()
{
  char ch='a';
  double val = nan(&ch);
  if(isnan(val))
     cout << "isnan" << endl;

  return 0;
}

выход: isnan


Мне кажется, что лучшим действительно кросс-платформенным подходом было бы использовать объединение и проверить битовый шаблон double для проверки NaNs.

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

#include <stdint.h>
#include <stdio.h>

union NaN
{
    uint64_t bits;
    double num;
};

int main()
{
    //Test if a double is NaN
    double d = 0.0 / 0.0;
    union NaN n;
    n.num = d;
    if((n.bits | 0x800FFFFFFFFFFFFF) == 0xFFFFFFFFFFFFFFFF)
    {
        printf("NaN: %f", d);
    }

    return 0;
}

стандарт IEEE говорит когда экспонента все 1С и мантисса не равна нулю, номер Нэн. Double - это 1 бит знака, 11 бит экспоненты и 52 бита мантиссы. Проверь немного.


Как комментарии выше состояния a != a не будет работать в g++ и некоторых других компиляторах, но этот трюк должен. Это может быть не так эффективно, но это все еще способ:

bool IsNan(float a)
{
    char s[4];
    sprintf(s, "%.3f", a);
    if (s[0]=='n') return true;
    else return false;
}

в основном, в g++ (я не уверен в других) printf печатает " nan " на %d или %.F форматирует, если переменная не является допустимым целым числом / float. Поэтому этот код проверяет, что первый символ строки должен быть "n "(как в"nan")