No C ++ 14, existem várias maneiras de testar se um número de ponto flutuante value
é um NaN.
Dessas formas, apenas a verificação dos bits da representação do número funciona de maneira confiável, conforme observado na minha resposta original. Em particular, std::isnan
e a verificação frequentemente propostav != v
, não funciona de maneira confiável e não deve ser usada, para que seu código pare de funcionar corretamente quando alguém decide que a otimização de ponto flutuante é necessária e solicita que o compilador faça isso. Essa situação pode mudar, os compiladores podem ficar mais conformes, mas para esse problema que não aconteceu nos 6 anos desde a resposta original.
Por cerca de 6 anos, minha resposta original foi a solução selecionada para esta pergunta, o que foi bom. Mas, recentemente, uma resposta altamente votada, recomendando o v != v
teste não confiável , foi selecionada. Portanto, essa resposta adicional mais atualizada (agora temos os padrões C ++ 11 e C ++ 14 e C ++ 17 no horizonte).
As principais maneiras de verificar o NaN-ness, a partir do C ++ 14, são:
std::isnan(value) )
é a maneira de biblioteca padrão pretendida desde C ++ 11. isnan
aparentemente entra em conflito com a macro Posix de mesmo nome, mas na prática isso não é um problema. O principal problema é que, quando a otimização aritmética de ponto flutuante é solicitada, pelo menos um compilador principal, ou seja, g ++, std::isnan
retorna false
para o argumento NaN .
(fpclassify(value) == FP_NAN) )
Sofre do mesmo problema que std::isnan
, ou seja, não é confiável.
(value != value) )
Recomendado em muitas respostas SO. Sofre do mesmo problema que std::isnan
, ou seja, 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 o comportamento otimizado, pode detectar NaNs (devido ao código otimizado apenas comparando diretamente as representações de nível de bit) e talvez combinado com outra maneira de cobrir o comportamento não otimizado padrão , pode detectar de forma confiável o NaN. Infelizmente, acabou por não funcionar de forma confiável.
(ilogb(value) == FP_ILOGBNAN) )
Sofre do mesmo problema que std::isnan
, ou seja, não é confiável.
isunordered(1.2345, value) )
Sofre do mesmo problema que std::isnan
, ou seja, não é confiável.
is_ieee754_nan( value ) )
Esta não é uma função padrão. É a verificação dos bits de acordo com o padrão IEEE 754. É completamente confiável mas o código depende um pouco do sistema.
No código de teste completo a seguir, “success” é se uma expressão relata Nan-ness do valor. Para a maioria das expressões, essa medida de sucesso, o objetivo de detectar NaNs e apenas NaNs, corresponde à sua semântica padrão. Para a (value == Fp_info::quiet_NaN()) )
expressão, no entanto, o comportamento padrão é que ele não funciona como um detector de 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 ) );
}
Resultados com g ++ (observe novamente que o comportamento padrão de (value == Fp_info::quiet_NaN())
é que ele não funciona como um detector de NaN, é apenas um interesse prático aqui):
[C: \ meus \ fóruns \ so \ 282 (detectar NaN)]
> g ++ --versão | encontre "++"
g ++ (x86_64-win32-sjlj-rev1, criado pelo projeto MinGW-W64) 6.3.0
[C: \ meus \ fóruns \ so \ 282 (detectar NaN)]
> g ++ foo.cpp && a
O compilador reivindica IEEE 754 = true
v = nan, (std :: isnan (valor)) = verdadeiro Sucesso
u = 3.14, (std :: isnan (valor)) = false Sucesso
w = inf, (std :: isnan (valor)) = false Sucesso
v = nan, ((fpclassify (value) == 0x0100)) = verdadeiro sucesso
u = 3.14, ((fpclassify (valor) == 0x0100)) = false Sucesso
w = inf, ((fpclassify (value) == 0x0100)) = false Sucesso
v = nan, ((valor! = valor)) = verdadeiro sucesso
u = 3,14, ((valor! = valor)) = falso Sucesso
w = inf, ((valor! = valor)) = falso Sucesso
v = nan, ((valor == Fp_info :: quiet_NaN ())) = false FAILED
u = 3.14, ((valor == Fp_info :: quiet_NaN ())) = false Sucesso
w = inf, ((valor == Fp_info :: quiet_NaN ())) = false Sucesso
v = nan, ((ilogb (value) == ((int) 0x80000000))) = verdadeiro sucesso
u = 3.14, ((ilogb (valor) == ((int) 0x80000000)))) = sucesso falso
w = inf, ((ilogb (value) == ((int) 0x80000000))) = false Sucesso
v = nan, (isunordered (1.2345, value)) = verdadeiro Sucesso
u = 3.14, (sem ordem (1.2345, valor)) = false Sucesso
w = inf, (isunordered (1.2345, value)) = false Sucesso
v = nan, (is_ieee754_nan (valor)) = verdadeiro Sucesso
u = 3.14, (is_ieee754_nan (valor)) = false Sucesso
w = inf, (is_ieee754_nan (value)) = false Sucesso
[C: \ meus \ fóruns \ so \ 282 (detectar NaN)]
> g ++ foo.cpp -ffast-math && a
O compilador reivindica IEEE 754 = true
v = nan, (std :: isnan (valor)) = falso FAILED
u = 3.14, (std :: isnan (valor)) = false Sucesso
w = inf, (std :: isnan (valor)) = false Sucesso
v = nan, ((fpclassify (valor) == 0x0100)) = falso FAILED
u = 3.14, ((fpclassify (valor) == 0x0100)) = false Sucesso
w = inf, ((fpclassify (value) == 0x0100)) = false Sucesso
v = nan, ((valor! = valor)) = falso FAILED
u = 3,14, ((valor! = valor)) = falso Sucesso
w = inf, ((valor! = valor)) = falso Sucesso
v = nan, ((valor == Fp_info :: quiet_NaN ())) = verdadeiro Sucesso
u = 3.14, ((valor == Fp_info :: quiet_NaN ())) = verdadeiro FAILED
w = inf, ((valor == Fp_info :: quiet_NaN ())) = verdadeiro FAILED
v = nan, ((ilogb (value) == ((int) 0x80000000))) = verdadeiro sucesso
u = 3.14, ((ilogb (valor) == ((int) 0x80000000)))) = sucesso falso
w = inf, ((ilogb (value) == ((int) 0x80000000))) = false Sucesso
v = nan, (isunordered (1.2345, value)) = false FAILED
u = 3.14, (sem ordem (1.2345, valor)) = false Sucesso
w = inf, (isunordered (1.2345, value)) = false Sucesso
v = nan, (is_ieee754_nan (valor)) = verdadeiro Sucesso
u = 3.14, (is_ieee754_nan (valor)) = false Sucesso
w = inf, (is_ieee754_nan (value)) = false Sucesso
[C: \ meus \ fóruns \ so \ 282 (detectar NaN)]
> _
Resultados com o Visual C ++:
[C: \ meus \ fóruns \ so \ 282 (detectar NaN)]
> cl / nologo- 2> & 1 | encontre "++"
Microsoft (R) C / C ++ Optimizing Compiler versão 19.00.23725 para x86
[C: \ meus \ fóruns \ so \ 282 (detectar NaN)]
> cl foo.cpp / fev &&b
foo.cpp
O compilador reivindica IEEE 754 = true
v = nan, (std :: isnan (valor)) = verdadeiro Sucesso
u = 3.14, (std :: isnan (valor)) = false Sucesso
w = inf, (std :: isnan (valor)) = false Sucesso
v = nan, ((fpclassify (value) == 2)) = verdadeiro sucesso
u = 3,14, ((fpclassify (valor) == 2)) = falso Sucesso
w = inf, ((fpclassify (value) == 2)) = false Sucesso
v = nan, ((valor! = valor)) = verdadeiro sucesso
u = 3,14, ((valor! = valor)) = falso Sucesso
w = inf, ((valor! = valor)) = falso Sucesso
v = nan, ((valor == Fp_info :: quiet_NaN ())) = false FAILED
u = 3.14, ((valor == Fp_info :: quiet_NaN ())) = false Sucesso
w = inf, ((valor == Fp_info :: quiet_NaN ())) = false Sucesso
v = nan, ((ilogb (valor) == 0x7fffffff)) = verdadeiro sucesso
u = 3.14, ((ilogb (valor) == 0x7fffffff)) = falso Sucesso
w = inf, ((ilogb (valor) == 0x7fffffff)) = verdadeiro FALHOU
v = nan, (isunordered (1.2345, value)) = verdadeiro Sucesso
u = 3.14, (sem ordem (1.2345, valor)) = false Sucesso
w = inf, (isunordered (1.2345, value)) = false Sucesso
v = nan, (is_ieee754_nan (valor)) = verdadeiro Sucesso
u = 3.14, (is_ieee754_nan (valor)) = false Sucesso
w = inf, (is_ieee754_nan (value)) = false Sucesso
[C: \ meus \ fóruns \ so \ 282 (detectar NaN)]
> cl foo.cpp / Feb / fp: fast && b
foo.cpp
O compilador reivindica IEEE 754 = true
v = nan, (std :: isnan (valor)) = verdadeiro Sucesso
u = 3.14, (std :: isnan (valor)) = false Sucesso
w = inf, (std :: isnan (valor)) = false Sucesso
v = nan, ((fpclassify (value) == 2)) = verdadeiro sucesso
u = 3,14, ((fpclassify (valor) == 2)) = falso Sucesso
w = inf, ((fpclassify (value) == 2)) = false Sucesso
v = nan, ((valor! = valor)) = verdadeiro sucesso
u = 3,14, ((valor! = valor)) = falso Sucesso
w = inf, ((valor! = valor)) = falso Sucesso
v = nan, ((valor == Fp_info :: quiet_NaN ())) = false FAILED
u = 3.14, ((valor == Fp_info :: quiet_NaN ())) = false Sucesso
w = inf, ((valor == Fp_info :: quiet_NaN ())) = false Sucesso
v = nan, ((ilogb (valor) == 0x7fffffff)) = verdadeiro sucesso
u = 3.14, ((ilogb (valor) == 0x7fffffff)) = falso Sucesso
w = inf, ((ilogb (valor) == 0x7fffffff)) = verdadeiro FALHOU
v = nan, (isunordered (1.2345, value)) = verdadeiro Sucesso
u = 3.14, (sem ordem (1.2345, valor)) = false Sucesso
w = inf, (isunordered (1.2345, value)) = false Sucesso
v = nan, (is_ieee754_nan (valor)) = verdadeiro Sucesso
u = 3.14, (is_ieee754_nan (valor)) = false Sucesso
w = inf, (is_ieee754_nan (value)) = false Sucesso
[C: \ meus \ fóruns \ so \ 282 (detectar NaN)]
> _
Resumindo os resultados acima, apenas o teste direto da representação no nível de bit, usando a is_ieee754_nan
função definida neste programa de teste, funcionou de maneira confiável em todos os casos com o g ++ e o Visual C ++.
Adendo:
Depois de postar o texto acima, tomei conhecimento de mais um possível teste para NaN, mencionado em outra resposta aqui, a saber ((value < 0) == (value >= 0))
. Isso acabou funcionando bem com o Visual C ++, mas falhou com a -ffast-math
opção do g ++ . Somente testes diretos de bitpattern funcionam de maneira confiável.