Pergunta

Existe uma função isnan ()?

PS .: Eu estou no MinGW (se isso faz uma diferença).

Eu tinha isso resolvido usando isnan () de <math.h>, que não existe no <cmath>, que eu estava #includeing em primeiro lugar.

Foi útil?

Solução

De acordo com o padrão IEEE, valores NaN tem a propriedade estranho que comparações envolvendo deles são sempre false. Ou seja, para um flutuador f, f != f será true única se f é NaN.

Note que, como alguns comentários abaixo temos a pontas, nem todos os compiladores respeitar isso quando otimização de código.

Para qualquer compilador que pretende usar IEEE ponto flutuante, este truque deve trabalho. Mas eu não posso garantir que ele irá trabalho na prática. Verifique com o seu compilador, em caso de dúvida.

Outras dicas

Não há nenhuma função isnan() disponível na biblioteca padrão atual C ++. Foi introduzido em C99 e definido como um macro não uma função. Elementos da biblioteca padrão definidos pelo C99 não fazem parte do atual C ++ padrão ISO / IEC 14882: 1998 nem a sua actualização ISO / IEC 14882:., 2003

Em 2005 Relatório Técnico 1 foi proposto. O TR1 traz compatibilidade com C99 a C ++. Apesar do fato de que nunca foi oficialmente adotado para se tornar C ++ padrão, muitos ( GCC 4.0 + ou Visual C ++ 9.0+ C ++ implementações que fornecem recursos TR1 , todos ou apenas alguns (Visual C ++ 9.0 não fornece funções matemáticas C99).

Se TR1 está disponível, então cmath inclui C99 elementos como isnan(), isfinite(), etc. mas eles são definidos como funções, não macros, geralmente em namespace std::tr1::, embora muitas implementações (ou seja CCG 4+ no Linux ou no Xcode no Mac OS X 10.5+) injetá-las diretamente para std::, então std::isnan está bem definida.

Além disso, algumas implementações de C ++ ainda fazer C99 isnan() macro disponível para C ++ (incluído através cmath ou math.h), o que pode causar mais confusão e desenvolvedores podem assumir que é um comportamento padrão.

Uma nota sobre Viusal C ++, como mencionado acima, ele não fornece std::isnan nem std::tr1::isnan, mas fornece uma função de extensão definida como _isnan() que está disponível desde Visual C ++ 6.0

No XCode, existe ainda mais divertido. Tal como mencionado, define GCC 4+ std::isnan. Para versões mais antigas do compilador e da biblioteca XCode forma, parece (aqui é discussão relevante), não tive oportunidade de verificar eu mesmo) duas funções são definidas, __inline_isnand() em Intel e __isnand() em Power PC.

Primeira solução: se você estiver usando C ++ 11

Uma vez que este foi solicitado havia um pouco de novos desenvolvimentos: é importante saber que std::isnan() é parte do C ++ 11

Sinopse

Definido no cabeçalho <cmath>

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

determina se o ponto flutuante dado número arg é não-numérico (NaN).

Parâmetros

arg: valor de ponto flutuante

Valor de retorno

true se arg é NaN, false caso contrário

Referência

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

Por favor note que este é incompatível com -fast-matemática, se você usar g ++, veja abaixo outras sugestões.


Outras soluções: se você usar non C ++ 11 ferramentas compatíveis

Para C99, em C, este é implementado como uma macro retorna isnan(c)that um valor int. O tipo de x será float, double ou long double.

Vários fornecedores pode ou não incluir ou não uma função isnan().

A forma supostamente portátil para verificar se há NaN é usar o 754 propriedades IEEE que NaN não é igual a si mesmo:. Ou seja x == x será falso para x sendo NaN

No entanto, a última opção pode não funcionar com todos os compilador e algumas configurações (particularmente otimização configurações), portanto, em último recurso, você pode sempre verificar o padrão de bits ...

Há também um cabeçalho somente biblioteca presente no impulso que tem boas ferramentas para lidar com flutuante tipos de dados de ponto

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

Você tem as seguintes funções:

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);

Se você tiver tempo, em seguida, ter um olhar para todo kit de ferramentas de matemática do Boost, que tem muitas ferramentas úteis e está crescendo rapidamente.

Além disso, quando lidar com flutuantes e não flutuantes pontos que poderia ser uma boa idéia para olhar para o numérico Conversões .

Existem três formas "oficiais": posix isnan macro , c ++ 0x função isnan template , ou Visual C ++ função _isnan .

Infelizmente é bastante prático para detectar qual deles para usar.

E, infelizmente, não há nenhuma maneira confiável para detectar se você tem IEEE 754 representação com NaNs. A biblioteca padrão oferece uma forma oficial (numeric_limits<double>::is_iec559). Mas, em compiladores prática como g ++ parafuso que up.

Em teoria, pode-se usar simplesmente x != x fortes, mas compiladores tais como g ++ e visual c ++ screw.

Assim, no final, para o teste específico bitpatterns NaN , assumindo (e, esperamos fazer cumprir, em algum momento!) Uma representação particular tal como IEEE 754.


Editar : como um exemplo de "compiladores, como g ++ ... parafuso que up", considere

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

Compilação com 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> _

Há um std :: isnan se você compilador suportes C99 extensões, mas eu não tenho certeza se mingw faz.

Aqui está uma pequena função que deve funcionar se o seu compilador não tem a função padrão:

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

Você pode usar numeric_limits<float>::quiet_NaN( ) definido na biblioteca padrão limits para teste com. Há uma constante separado definido para 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;
}

Eu não sei se isso funciona em todas as plataformas, como eu só testados com g ++ em Linux.

Você pode usar a função isnan(), mas você precisa incluir a biblioteca C matemática.

#include <cmath>

Como esta função é parte do C99, ele não está disponível em todos os lugares. Se o fornecedor não fornecer a função, você também pode definir a sua própria variante de compatibilidade.

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

prevenção nan

A minha resposta a esta pergunta é não usam cheques retroativos para nan . Use preventivas verifica a existência de divisões da forma 0.0/0.0 em seu lugar.

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

Resultados nan do 0.f/0.f operação, ou 0.0/0.0. nan é um terrível inimigo para a estabilidade do seu código que deve ser detectado e impedido com muito cuidado 1 . As propriedades de nan que são diferentes dos números normais:

  • nan é tóxico, (5 * nan = nan)
  • nan não é igual a nada, nem mesmo a própria (nan! = nan)
  • nan não é maior do que qualquer coisa (nan!> 0)
  • nan não é menos do que nada (nan! <0)

Os últimos 2 propriedades listadas são contra-lógica e irá resultar em comportamento estranho de código que se baseia em comparações com um número nan (3º última propriedade é estranho também, mas você provavelmente não está indo cada vez para ver x != x ? em seu código (a menos que você está verificando para nan (unreliably))).

Em meu próprio código, notei que valores nan tendem a produzir difícil encontrar bugs. (Note como este é não o caso para inf ou -inf. (-inf <0) retorna TRUE, (0 <inf) retorna TRUE, e até mesmo (-inf <inf) retorna TRUE. Então, na minha experiência, o comportamento do código é muitas vezes ainda como desejado).

o que fazer sob nan

O que você quer que aconteça sob 0.0/0.0 deve ser tratado como um caso especial , mas o que você faz deve depender dos números que você espera para sair do código.

No exemplo acima, o resultado de (0.f/FLT_MIN) será 0, basicamente. Você pode querer 0.0/0.0 para gerar HUGE vez. Então,

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.

Assim, no exemplo acima, se x foram 0.f, inf resultaria (que tem muito bom / comportamento não-destrutivos, como mencionado acima, na verdade).

Lembre-se, inteiro divisão por 0 provoca uma exceção tempo de execução. Portanto, você deve sempre verificar se há divisão por 0. Só porque 0.0/0.0 avalia silenciosamente para nan não significa que você pode ser preguiçoso e não verificar se há 0.0/0.0 antes que aconteça.

1 Verifica nan via x != x às vezes são pouco fiáveis ??(x != x sendo retirados por alguns compiladores de otimização que quebra o cumprimento IEEE, especificamente quando o interruptor -ffast-math está habilitado).

O código a seguir usa a definição de NAN (todos os bits expoente definidos, pelo menos, um conjunto de bits fracionário) e assume que sizeof (int) = sizeof (float) = 4. Você pode olhar para cima NAN na Wikipedia para os detalhes.

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

a partir de C ++ 14 há um certo número de maneiras para teste se um número de ponto flutuante value é um NaN.

Destas maneiras, única verificação dos bits de representação do número, funciona de forma confiável, como observado na minha resposta inicial. Em particular, std::isnan eo v != v verificação muitas vezes proposto, não funcionam de forma confiável e não deve ser usado, para que seu código pára de funcionar corretamente quando alguém decide que a otimização de ponto flutuante é necessária, e solicita ao compilador para fazer isso. Esta situação pode mudar, compiladores pode obter mais conformes, mas para este problema que não aconteceu nos 6 anos desde que a resposta original.

Para cerca de 6 anos minha resposta inicial foi a solução escolhida para esta pergunta, que foi OK. Mas, recentemente, uma resposta altamente upvoted recomendando o teste v != v confiável foi selecionado. Daí esta resposta adicional mais up-to-date (agora temos a C ++ 11 e C ++ 14 padrões e C ++ 17 no horizonte).


As principais maneiras para verificar NaN-ness, a partir de C ++ 14 são:

  • std::isnan(value) )
    é a pretendida forma biblioteca padrão desde C ++ 11. isnan aparentemente em conflito com o Posix macro com o mesmo nome, mas na prática isso não é um problema. O principal problema é que, quando flutuando optimização aritmética de ponto é solicitada, em seguida, com, pelo menos, um compilador principal, ou seja, g ++, std::isnan <> fortes retornos false para argumento NaN .

  • (fpclassify(value) == FP_NAN) )
    Sofre com o mesmo problema que std::isnan, ou seja, não é confiável.

  • (value != value) )
    Recomendado em muitas respostas lo. Sofre do mesmo problema que std::isnan, isto é, não é confiável.

  • (value == Fp_info::quiet_NaN()) )
    Este é um teste que com o comportamento padrão não deve detectar NaNs, mas que, com a comportamento otimizado talvez poderia detectar NaNs (devido ao código otimizado apenas comparando o representações bitlevel directamente), e talvez combinada com uma outra forma de cobrir o comportamento padrão optimizado-un, podia detectar de forma fiável NaN. Infelizmente ele acabou por não funcionar de forma confiável.

  • (ilogb(value) == FP_ILOGBNAN) )
    Sofre com o mesmo problema que std::isnan, ou seja, não é confiável.

  • isunordered(1.2345, value) )
    Sofre com o mesmo problema que std::isnan, ou seja, não é confiável.

  • is_ieee754_nan( value ) )
    Esta não é uma função padrão. É verificação dos bits de acordo com o IEEE 754 padrão. É completamente confiável e o código é um pouco dependente do sistema.


No “sucesso” seguinte código de teste completo é se uma expressão relata Nan-ness do valor. Para a maioria das expressões dessa medida de sucesso, o objetivo de detectar NaNs e apenas NaNs, corresponde à sua semântica padrão. Para a expressão (value == Fp_info::quiet_NaN()) ), no entanto, o comportamento padrão é que ele não funciona como um NaN-detector.

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

Os resultados com g ++ (note novamente que o comportamento padrão de (value == Fp_info::quiet_NaN()) é que ele não funciona como um NaN-detector, é apenas muito de interesse prático aqui):

[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)]
> _

Os resultados com 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)]
> _

Resumindo os resultados acima, apenas o teste directo da representação de nível de bits, usando a função is_ieee754_nan definido neste programa de teste, trabalhou de forma fiável em todos os casos, com ambos g ++ e Visual C ++.


Adenda:
Depois de postar o acima me dei conta de mais um possível teste para NaN, mencionado em outra resposta aqui, ou seja, ((value < 0) == (value >= 0)). Que acabou por funcionar bem com o Visual C ++, mas não com g ++ 's -ffast-math opção. Somente testes bitpattern direta funciona de forma confiável.

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

Isso funciona se sizeof(int) é 4 e sizeof(long long) é 8.

Durante o tempo de execução é apenas comparação, castings não tomar qualquer momento. Apenas muda sinalizadores de comparação de configuração para verificar a igualdade.

Uma solução possível que não dependem do específico representação IEEE para NaN usado seria o seguinte:

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

Quanto a mim a solução poderia ser uma macro para torná-lo explicitamente em linha e, portanto, rápido o suficiente. Ele também funciona para qualquer tipo float. Ele baseia-se no fato de que o único caso em que um valor não for igual a si mesmo é quando o valor não é um número.

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

Tendo em conta que nem sempre é garantido por NaN (tal como se estivesse usando a opção -ffast-matemática), eu tenho usado (x = x!):

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

Os números não podem ser tanto <0 e> = 0, então realmente este cheque só passa se o número não é nem menos do que, nem maior ou igual a zero. Que é basicamente nenhum número em tudo, ou NaN.

Você também pode usar isso se você prefere:

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

Eu não sei como isso é afetado por -ffast-matemática, porém, assim que sua milhagem pode variar.

Isso funciona:

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

saída: isnan

Parece-me que a melhor abordagem verdadeiramente multi-plataforma seria a utilização de uma união e para testar o padrão da dupla pouco para verificar se há NaNs.

Eu não exaustivamente testado esta solução, e pode haver uma maneira mais eficiente de trabalhar com os padrões de bits, mas eu acho que ele deve funcionar.

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

O padrão IEEE diz quando o expoente é todos 1s e a mantissa não é zero, o número é um NaN. Duplo é pouco sinal 1, pedaços 11 expoente e pedaços 52 mantissa. Fazer uma verificação pouco.

em x86-64 você pode ter métodos extremamente rápidos para verificação de NaN e infinito, que trabalham independentemente opção do compilador -ffast-math de. (f != f, std::isnan, std::isinf sempre produzir false com -ffast-math).


Os testes para NaN, infinito e números finitos pode ser facilmente feito através da verificação para o máximo expoente. infinito é expoente máximo com mantissa zero, NaN é máximo expoente e diferente de zero mantissa. O expoente é armazenado nos próximos bits após o bit de sinal superior, para que possamos só deixou mudança para se livrar do bit de sinal e fazer o expoente os bits de nível superior, sem mascaramento (operator&) é necessário:

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

As versões std de isinf e isfinite carga 2 constantes double/float de segmento .data e no pior dos casos eles podem causar 2 erros de cache de dados. As versões anteriores não carregar quaisquer dados, constantes inf_double_shl1 e inf_float_shl1 se codificado como operandos imediatos para as instruções de montagem.


Faster isnan2 está a apenas 2 instruções de montagem:

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

Usa o fato de que ucomisd conjuntos de instruções bandeira paridade se qualquer argumento é NaN. Isto é como std::isnan funciona quando há opções -ffast-math é especificado.

Este detecta o infinito e também NaN no Visual Studio, verificando que está dentro dos limites duplas:

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

Como comentários anteriores estado a! = A não funcionará em g ++ e alguns outros compiladores, mas isso deve truque. Pode não ser tão eficiente, mas ainda é uma maneira:

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

Basicamente, em g ++ (não tenho certeza sobre os outros embora) printf imprime 'nan' em% d ou formatos% .f se a variável não é um válido inteiro / float. Portanto, este código está verificando o primeiro caractere de string para ser 'n' (como em "nan")

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top