Как проверить isnan в C-ядре TensorFlow? [Дубликат]

Как будто вы пытаетесь получить доступ к объекту, который является null. Рассмотрим ниже пример:

TypeA objA;

. В это время вы только что объявили этот объект, но не инициализировали или не инициализировали. И всякий раз, когда вы пытаетесь получить доступ к каким-либо свойствам или методам в нем, он будет генерировать NullPointerException, что имеет смысл.

См. Также этот пример:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
330
задан Peter Mortensen 3 September 2014 в 20:55
поделиться

20 ответов

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

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

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

315
ответ дан jalf 18 August 2018 в 15:46
поделиться
  • 1
    Компилятор лучше не удалять это при работе в режиме IEEE. Конечно, проверьте документацию для своего компилятора ... – dmckee 20 February 2009 в 20:21
  • 2
    -1 работает только в теории, а не на практике: компиляторы, такие как g ++ (с -fastmath), закручивают это. единственный общий способ, до c ++ 0x, - проверить битпаттерн. – Cheers and hth. - Alf 26 March 2011 в 10:15
  • 3
    @Alf: Документация для опции -ffast-math явно говорит о том, что это может привести к некорректному выходу для программ, которые зависят от точной реализации, если правила / спецификации IEEE или ISO для математических функций. Без использования этой опции использование x != x является вполне допустимым и переносимым способом тестирования для NaN. – Adam Rosenfield 26 March 2011 в 23:37
  • 4
    @Adam: в документации открыто указано, что она не соответствует требованиям, да. и да, я уже столкнулся с этим доводом, подробно обсудив это с Габриэлем Досом Рейсом. он обычно используется для защиты дизайна, в круговом аргументе (я не знаю, если вы намереваетесь ассоциироваться с этим, но стоило бы узнать об этом - это материал для пламенной войны). ваш вывод о том, что x != x действителен без этой опции, не следует логически. это может быть верно для конкретной версии g ++, или нет. во всяком случае, вы вообще не можете гарантировать, что опция fastmath не будет использоваться. – Cheers and hth. - Alf 27 March 2011 в 05:48
  • 5
    @Alf: Нет, я не знал о вашей дискуссии с Габриэлем Досом Рейсом. Стив Джессоп сделал большой аргумент в другом вопросе о принятии представления IEEE. Если вы принимаете IEEE 754 и что компилятор работает соответствующим образом (то есть без опции -ffast-math), тогда x != x является допустимым и переносимым решением. Вы можете даже проверить на -ffast-math путем тестирования макроса __FAST_MATH__ и в этом случае переключиться на другую реализацию (например, использовать союзы и бит-скрининг). – Adam Rosenfield 28 March 2011 в 00:45

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

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

81
ответ дан Anonymous 18 August 2018 в 15:46
поделиться
  • 1
    Благодаря! Только то, что я искал. – Dr. Watson 27 April 2011 в 20:37
  • 2
    он был добавлен в Boost 1.35 (я только что нашел, что моя программа не компилируется на старом дистрибутиве linux). – marcin 20 May 2012 в 21:38
  • 3
    если вы скомпилируете с помощью опции -fast-math, тогда эта функция не будет работать должным образом. – Gaetano Mendola 16 November 2012 в 12:59

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

25
ответ дан Bill the Lizard 18 August 2018 в 15:46
поделиться
  • 1
    Остерегайтесь, хотя, как представляется, ошибка в numeric_limits в GCC версии 3.2.3, так как она возвращает 0.0 для quiet_NaN. Более поздние версии GCC хороши в моем опыте. – Nathan Kitchen 30 April 2009 в 22:12
  • 2
    @Nathan: Приятно знать. Я использую версию 4.3.2, поэтому я хорошо из леса. – Bill the Lizard 30 April 2009 в 22:48

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

0
ответ дан bop 18 August 2018 в 15:46
поделиться

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

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

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

В теории можно было бы просто использовать x != x, но компиляторы, такие как g ++ и визуальный c ++-винт, вверху.

Итак, в end, протестировать конкретные битпаттеры 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's support team for more information.
!failed

C:\test> _
42
ответ дан Cheers and hth. - Alf 18 August 2018 в 15:46
поделиться
  • 1
    @Alf: Ваш пример работает как ожидается для меня как в Mac OS X, так и в Linux на разных версиях g ++ между 4.0 и 4.5. В документации для опции -ffast-math явно указано, что это может привести к некорректному выходу для программ, которые зависят от точной реализации, если правила / спецификации IEEE или ISO для математических функций. Без использования этой опции использование x != x является вполне допустимым и переносимым способом тестирования для NaN. – Adam Rosenfield 26 March 2011 в 23:34
  • 2
    @Adam: вам не хватает того, что стандарт C ++ не требует представления IEEE или математики для поплавков. Что касается справочной страницы, gcc -ffast-math по-прежнему является совместимой реализацией C ++ (ну, если предположить, что он прав numeric_limits::is_iec559, это, хотя Alf предлагает выше, что это не так): код на C ++, основанный на IEEE, является < i> not портативный C ++ и не имеет права ожидать реализации, чтобы обеспечить его. – Steve Jessop 27 March 2011 в 01:00
  • 3
    И правильное решение Alf, быстрое испытание gcc 4.3.4 и is_iec559 истинно с -ffast-math. Таким образом, проблема заключается в том, что в документах GCC для -ffast-math говорят только, что это не-IEEE / ISO для математических функций, тогда как они должны сказать, что это не C ++, поскольку его реализация numeric_limits BORKED. Я бы предположил, что GCC не всегда может сказать в то время, когда этот шаблон определен, имеет ли фактический бэкэнд соответствующие поплавки, и поэтому даже не пытается. IIRC есть аналогичные проблемы в выдающемся списке ошибок для соответствия GCC C99. – Steve Jessop 27 March 2011 в 01:05
  • 4
    @Alf, @Steve, я не знал, что стандарт C ++ не имеет спецификации о значениях с плавающей запятой. Это очень шокирует меня. Он лучше обрабатывает IEEE 754 и NaN в качестве расширения для конкретной платформы, а не стандарта. Не так ли? И могу ли я ожидать, что любой вид isnan () или IEEE754 добавлен в C ++ 0x? – Eonil 27 March 2011 в 01:14
  • 5
    @Eonil: C ++ 0x по-прежнему имеет, например, «представление значений типов флагов-точек», определяемое реализацией ». C и C ++ предназначены для поддержки реализаций на машинах без аппаратного обеспечения с плавающей точкой, и правильные поплавки IEEE 754 могут быть довольно медленными для эмуляции, чем разумно точные альтернативы. Теория заключается в том, что вы можете утверждать is_iec559, если вам нужен IEEE, на практике это, похоже, не работает на GCC. C ++ 0x имеет функцию isnan, но поскольку GCC теперь неправильно реализует is_iec559, я думаю, что он тоже не будет в C ++ 0x, а -ffast-math может сломать его isnan. – Steve Jessop 27 March 2011 в 11:40

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

Из этих способов проверяется только проверка битов представления числа, работает надежно, как отмечено в моем первоначальном ответе. В частности, 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()) ) Это тест, который при стандартном поведении не должен определять NaN, но что с оптимизированным поведением возможно обнаружить NaNs ( из-за оптимизированного кода, непосредственно сравнивающего представления bitlevel) и, возможно, в сочетании с другим способом охвата стандартного не оптимизированного поведения, может надежно обнаружить NaN. К сожалению, оказалось, что он не работает надежно.
  • (ilogb(value) == FP_ILOGBNAN) ) Страдает от той же проблемы, что и std::isnan, т. Е. Ненадежна.
  • isunordered(1.2345, value) ) Страдает от той же проблемы как std::isnan, т. е. не является надежным.
  • is_ieee754_nan( value ) ) Это не стандартная функция. Это проверка бит в соответствии со стандартом IEEE 754. Это полностью надежный , но код несколько зависит от системы.

В следующем полном тестовом коде & ldquo; success & rdquo; является ли выражение сообщает об отсутствии значения. Для большинства выражений эта мера успеха, цель обнаружения 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 ++:

[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 C ++.


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

131
ответ дан Community 18 August 2018 в 15:46
поделиться
  • 1
    Спасибо за указание на это; Подобное программирование определенно поможет решить проблему как таковую. Но в следующий раз, пожалуйста, старайтесь не злоупотреблять функциями форматирования текста слишком много. Переключение размеров шрифтов, их веса и стиля делают его очень трудным для чтения. – Magnus 16 October 2013 в 14:03
  • 2
    Обратите внимание, что 0.0 / 0.0 не является единственной операцией, которая может привести к NaN. Квадратный корень отрицательного числа возвращает NaN. Косинус + бесконечности возвращает NaN. операция acos (x), где x не находится в диапазоне [0, pi], также может приводить к NaN. Вкратце, нужно быть особенно осторожным, чтобы также посмотреть на эти потенциально опасные операции, а не только на 0.0 / 0.0. – Boris 28 October 2014 в 23:20
  • 3
    Определенно заслуживает того, чтобы быть принятым ответом и заслуживает большего внимания. Спасибо за совет – LBes 4 September 2015 в 16:31
  • 4
    Полностью согласен с Борисом. По моему опыту, NaN практически всегда происходил из чего-то вроде sqrt (-1.302e-53), т. Е. Результаты промежуточного вычисления, близкие к нулю, подаются в sqrt без проверки на негативность. – hans_meine 15 September 2016 в 09:41
  • 5
    -1 std::isnan по-прежнему является неплохой рекомендацией по состоянию на февраль 2017 года, поскольку она не работает с оптимизацией с плавающей запятой g ++. – Cheers and hth. - Alf 9 February 2017 в 16:33
  • 6
    @ Cheersandhth.-Alf: этот вариант соответствует IEEE? Ответ отредактирован – BlueTrin 16 February 2017 в 10:59
  • 7
    @BlueTrin: Для работы IEEE 754 требуются как x != x, так и isnan. Что касается последнего, стандарт IEEE 754-2008 гласит, что «Реализации должны обеспечивать следующие не вычислительные операции для всех поддерживаемых арифметических форматов» и «isNaN (x) истинно тогда и только тогда, когда x является NaN». Для проверки соответствия стандарту требуются is754version1985() и is754version2008(), где C ++ вместо этого предлагает std::numeric_limits<Fp>::is_iec559() (IEC 559 - тот же стандарт). К сожалению, с оптимизацией -ffast-math, например. g ++, но не соответствует требованиям. – Cheers and hth. - Alf 16 February 2017 в 16:02
  • 8
    0 Удаление моего нижнего уровня, поскольку добавленный комментарий делает эту проблему понятной для читателя. :) – Cheers and hth. - Alf 16 February 2017 в 16:04

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

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

bool custom_isnan(double var)
{
    volatile double d = var;
    return d != d;
}
38
ответ дан CTT 18 August 2018 в 15:46
поделиться
  • 1
    – Brian R. Bondy 20 February 2009 в 19:42
  • 2
    При этом их вероятность заключается в том, что компилятор оптимизирует сравнение, всегда возвращая true. – CTT 20 February 2009 в 19:49
  • 3
    Нет, нет. Компилятор, который делает это, сломан. Вы могли бы также сказать, что существует вероятность того, что стандартная библиотека isnan вернет неверный результат. Технически верно, что компилятор может быть глючным, но на практике, не Gonna Happen. То же, что и var != var. Он работает, потому что это то, как определены значения с плавающей запятой IEEE. – jalf 23 January 2010 в 17:30
  • 4
    если установлено значение -ffast-math, isnan () не сможет вернуть правильный результат для gcc. Конечно, эта оптимизация документирована как нарушение семантики IEEE ... – Matthew Herrmann 24 January 2011 в 02:06
  • 5
    Если задана -ffast-math, тогда компилятор не работает. Вернее, если задана опция -ffast-math, все ставки отключены, и вы не можете полагаться на NaN в любом случае. – Adrian Ratnapala 21 July 2015 в 13:45

Возможное решение, которое не зависит от конкретного представления 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) );
}
4
ответ дан Dan Nathan 18 August 2018 в 15:46
поделиться

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

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

output: isnan

3
ответ дан edW 18 August 2018 в 15:46
поделиться

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

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

11
ответ дан Ian 18 August 2018 в 15:46
поделиться

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

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

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

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

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

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

4
ответ дан Jerramy 18 August 2018 в 15:46
поделиться
  • 1
    Это на самом деле ошибочно. f != f также испорчен. Я видел, что llvm оптимизирует почти идентичный код. Оптимизатор может распространять информацию о первом сравнении и вычислять, что второй сорт никогда не может быть правдой, если первый из них. (если компилятор строго подчиняется правилам IEEE f != f, намного проще) – Markus 21 March 2016 в 20:06
  • 2
    Не работает с опцией g ++ -ffast-math. Работает с Visual C ++. См. ( stackoverflow.com/a/42138465/464581 ). – Cheers and hth. - Alf 9 February 2017 в 16:39
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.

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

7
ответ дан Jonas Byström 18 August 2018 в 15:46
поделиться
  • 1
    Также обратите внимание, что он ограничен представлением IEEE 754. – Cheers and hth. - Alf 9 February 2017 в 16:41
  • 2
    Обратите внимание, что этот бросок нарушает правило строгого сглаживания g ++, и этот компилятор, как известно, делает Unmentionable Things ™, когда он обнаруживает формальный UB. Вместо эффективных трансляций, с g ++ вам нужно использовать memcpy через байтовый массив. Код для этого в ответе # 2 . – Cheers and hth. - Alf 16 February 2017 в 16:50

В текущей стандартной библиотеке 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() on Power PC.

203
ответ дан mloskot 18 August 2018 в 15:46
поделиться
  • 1
    Я хочу, чтобы ваш ответ появился в верхней части этой страницы ... – Anton Daneyko 12 April 2011 в 17:20
  • 2
    Все хотят такие функции, как isNan или isInfinity. Почему ответственные люди не просто включают в свои стандарты ???? - Я постараюсь выяснить, как взять на себя ответственность и внести свой голос за это. Шутки в сторону. – shuhalo 5 October 2011 в 10:04
  • 3
    @Martin: это часть C ++ 11 ru.cppreference.com/w/cpp/numeric/math/isnan – BlueTrin 30 May 2013 в 14:29
  • 4
    @shuhalo На самом деле? – Tomáš Zato 8 February 2016 в 10:15
  • 5
    Этот ответ должен быть обновлен, так как std::isnan теперь является частью стандарта C ++ 11, и поддержка была распространена. std :: isnan был реализован в Visual Studio, начиная с Visual Studio 2013. Может быть, @shuhalo взял на себя ответственность :-) – aberaud 14 May 2016 в 17:58

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

#include <cmath>

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

inline bool isnan(double x) {
    return x != x;
}
15
ответ дан raimue 18 August 2018 в 15:46
поделиться
  • 1
    Я использовал & lt; cmath & gt; и в нем нет ишнана! кстати, я обнаружил, что там есть a isnan в & lt; math.h & gt; – hasen 20 February 2009 в 19:41
  • 2
    Как я уже сказал, это часть C99. Поскольку C99 не является частью любого существующего стандарта C ++, я предоставил альтернативу. Но поскольку вполне вероятно, что isnan () будет включен в следующий C ++-стандарт, я помещаю в него директиву #ifndef. – raimue 21 February 2009 в 02:31

Мне кажется, что лучший по-настоящему кросс-платформенный подход состоял бы в том, чтобы использовать объединение и протестировать битовую схему двойника для проверки 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;
}
1
ответ дан Sheldon Juncker 18 August 2018 в 15:46
поделиться

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

#ifndef isnan
  #define isnan(a) (a != a)
#endif
4
ответ дан user1705817 18 August 2018 в 15:46
поделиться
  • 1
    Это один из лучших ответов на этот вопрос! Спасибо, что поделились. – Henri Menke 6 September 2013 в 12:13
  • 2
    Некоторые компиляторы могут оптимизировать его из кода. – ST3 3 October 2013 в 11:32
  • 3
    Другие ответы указывают на то, что это может завершиться неудачей с помощью опции -ffast-math. – Technophile 29 January 2016 в 23:23

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

/*
  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;
}
4
ответ дан William Andrew Burnson 18 August 2018 в 15:46
поделиться

Как указано выше, состояние 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 форматы, если variable не является допустимым целым числом / float. Поэтому этот код проверяет, чтобы первый символ строки был «n» (как в «nan»)

-3
ответ дан ZenJ 18 August 2018 в 15:46
поделиться
  • 1
    Разве это не приведет к переполнению буфера, если a = 234324.0f? – Mazyod 16 August 2013 в 03:18
  • 2
    Да t'will, или 340282346638528859811704183484516925440.000, если a = FLT_MAX. Он должен будет использовать char s[7]; sprintf(s, "%.0g", a);, который будет 6 chrs, если a=-FLT_MAX или -3e+38 – bobobobo 17 August 2013 в 01:32
133
ответ дан Community 7 September 2018 в 01:11
поделиться
135
ответ дан Community 30 October 2018 в 05:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: