Проверка, является ли двойной (или плавающий) NaN в С++

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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

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

Ответ 3

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

Поскольку это было задано, было несколько новых событий: важно знать, что std::isnan() является частью С++ 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, если arg NaN, false иначе

Ссылка

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

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


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

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

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

Предполагаемый переносимый способ проверки NaN заключается в использовании свойства IEEE 754, что NaN не равно самому себе: i.e. x == x будет false для x, являющегося NaN.

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

Ответ 4

Существует также библиотека только для заголовка, присутствующая в 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);

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

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

Ответ 5

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

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

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

В теории можно было бы просто просто x != x, но компиляторы, такие как g++ и visual С++, закручивали это.

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


EDIT: в качестве примера "компиляторы, такие как 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 support team for more information.
!failed

C:\test> _

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

#include <cmath>

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

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

Ответ 9

предотвращение накипи

Мой ответ на этот вопрос не использует ретроактивные проверки для 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)

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

В моем собственном коде я заметил, что значения nan, как правило, затрудняют поиск ошибок. (Обратите внимание, что это не так для inf или -inf. (-inf < 0) возвращает TRUE, (0 < inf) возвращает TRUE и даже (-inf < 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 включен).

Ответ 10

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

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

Ответ 11

В С++ 14 существует несколько способов проверить, является ли число с плавающей запятой value NaN.

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

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


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

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

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

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

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

  • (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()) ) стандартное поведение заключается в том, что он не работает как 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\so\282  (detect NaN)]
> g++ --version | find "++"
g++ (x86_64-win32-sjlj-rev1, Built by MinGW-W64 project) 6.3.0

[C:\my\forums\so\282  (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\so\282  (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\so\282  (detect NaN)]
> _

Результаты с Visual С++:

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

[C:\my\forums\so\282  (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\so\282  (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\so\282  (detect NaN)]
> _

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


Приложение:
После публикации выше я узнал о еще одном возможном испытании для NaN, упомянутого в другом ответе, а именно ((value < 0) == (value >= 0)). Это оказалось хорошо работать с Visual С++, но не с g++ -ffast-math. Только прямое тестирование битпатента работает надежно.

Ответ 12

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.

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

Ответ 13

Возможное решение, которое не будет зависеть от конкретного представления 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) );
}

Ответ 14

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

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

Номера не могут быть как < 0 и >= 0, так что эта проверка проверяется только тогда, если число не меньше или равно нулю или равно нулю. В основном это не число, или NaN.

Вы также можете использовать это, если хотите:

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

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

Ответ 15

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

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

Ответ 16

Это работает:

#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

Ответ 17

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

/*
  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;
}

Ответ 18

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

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

#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;
}

Ответ 19

Стандарт IEEE гласит, что когда показатель степени равен 1 с, а мантисса не равна нулю, число представляет собой NaN. Двойной является 1 знаковым битом, 11 экспонентными битами и 52 битами мантиссы. Сделайте немного проверки.

Ответ 20

На x86-64 у вас могут быть очень быстрые методы проверки NaN и бесконечности, которые работают независимо от опции компилятора -ffast-math. (f != f, std::isnan, std::isinf всегда дают false с -ffast-math).


Тестирование на NaN, бесконечность и конечные числа может быть легко выполнено, проверяя максимальный показатель степени. бесконечность - максимальный показатель с нулевой мантиссой, NaN - максимальный показатель и ненулевая мантисса. Экспонента сохраняется в следующих битах после самого верхнего знакового бита, так что мы можем просто сдвинуть влево, чтобы избавиться от знакового бита и сделать экспоненту самыми старшими битами, маскирование не требуется (operator&):

static inline uint64_t load_ieee754_rep(double a) {
    uint64_t r;
    static_assert(sizeof r == sizeof a, "Unexpected sizes.");
    std::memcpy(&r, &a, sizeof a); // Generates movq instruction.
    return r;
}

static inline uint32_t load_ieee754_rep(float a) {
    uint32_t r;
    static_assert(sizeof r == sizeof a, "Unexpected sizes.");
    std::memcpy(&r, &a, sizeof a); // Generates movd instruction.
    return r;
}

constexpr uint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);
constexpr uint32_t inf_float_shl1 = UINT32_C(0xff000000);

// The shift left removes the sign bit. The exponent moves into the topmost bits,
// so that plain unsigned comparison is enough.
static inline bool isnan2(double a)    { return load_ieee754_rep(a) << 1  > inf_double_shl1; }
static inline bool isinf2(double a)    { return load_ieee754_rep(a) << 1 == inf_double_shl1; }
static inline bool isfinite2(double a) { return load_ieee754_rep(a) << 1  < inf_double_shl1; }
static inline bool isnan2(float a)     { return load_ieee754_rep(a) << 1  > inf_float_shl1; }
static inline bool isinf2(float a)     { return load_ieee754_rep(a) << 1 == inf_float_shl1; }
static inline bool isfinite2(float a)  { return load_ieee754_rep(a) << 1  < inf_float_shl1; }

Версии std isinf и isfinite загружают 2 константы double/float из сегмента .data, и в худшем случае они могут вызвать 2 ошибки кэширования данных. Вышеприведенные версии не загружают никаких данных, константы inf_double_shl1 и inf_float_shl1 кодируются как непосредственные операнды в инструкции по сборке.


Быстрее isnan2 это всего лишь 2 инструкции по сборке:

bool isnan2(double a) {
    bool r;
    asm(".intel_syntax noprefix"
        "\n\t ucomisd %1, %1"
        "\n\t setp %b0"
        "\n\t .att_syntax prefix"
        : "=g" (r)
        : "x" (a)
        : "cc"
        );
    return r;
}

Использует тот факт, что инструкция ucomisd устанавливает флаг четности, если какой-либо аргумент равен NaN. Вот как работает std::isnan, когда не заданы опции -ffast-math.

Ответ 21

Это обнаруживает бесконечность, а также NaN в Visual Studio, проверяя, что оно в двойных пределах:

//#include <float.h>
double x, y = -1.1; x = sqrt(y);
if (x >= DBL_MIN && x <= DBL_MAX )
    cout << "DETECTOR-2 of errors FAILS" << endl;
else
    cout << "DETECTOR-2 of errors OK" << endl;

Ответ 22

Как указано выше, состояние 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" )