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

StackOverflow https://stackoverflow.com/questions/570669

  •  05-09-2019
  •  | 
  •  

Вопрос

Существует ли функция isnan()?

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

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

Это было полезно?

Решение

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

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

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

Другие советы

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

В 2005 году был предложен Технический отчет 1.TR1 обеспечивает совместимость с C99 на C ++.Несмотря на то, что он никогда не был официально принят в качестве стандарта C ++, многие (ССЗ 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() на компьютере с питанием.

Первое решение:если вы используете 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 с плавающей запятой not-a-number (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 ++, все испортили.

Итак, в конце концов, протестируйте для конкретного Наночастицы, предполагая (и, надеюсь, применяя, в какой-то момент!) конкретное представление, такое как 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 !< 0)

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

В моем собственном коде я заметил, что 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 переключатель включен).

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

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

Начиная с 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()) )
    Это тест, который со стандартной поведение не должны обнаружить Нанс, но что с оптимизировано поведение может обнаружить НАН (за счет оптимизированного кода, просто сравнив bitlevel представлений напрямую), и, возможно, в сочетании с еще одним способом крышка ООН-оптимизированное поведение, могут достоверно обнаружить Нэн.К сожалению оказалось, что это работает ненадежно.

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

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

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


В следующем полном тестовом коде “успех” означает, сообщает ли выражение о величине значения.Для большинства выражений этот показатель успеха, цель обнаружения NAN и только NAN, соответствует их стандартной семантике.Для (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()) заключается в том, что он не работает как нанодетектор, просто здесь он представляет очень большой практический интерес):

[C:\my\forums\so\282  (detect NaN)]
> g++ --версия | найти "++"
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-математика && 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 ++:

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

[C:\my\forums\so\282  (detect NaN)]
> cl foo.cpp /Февраль && 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 /Февраль /fp: быстро и& 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 C ++.


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

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.

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

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

Учитывая, что (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, поэтому ваш пробег может отличаться.

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

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

выходной сигнал:иснан

Мне кажется, что лучшим по-настоящему кроссплатформенным подходом было бы использовать объединение и протестировать битовый шаблон double для проверки наличия 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;
}

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

На x86-64 у вас могут быть чрезвычайно быстрые методы проверки NaN и infinity, которые работают независимо от -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 параметры указаны.

Это обнаруживает infinity, а также 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;

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top