A partir de C ++ 14, hay varias formas de probar si un número de coma flotante value
es un NaN.
De estas formas, solo la comprobación de los bits de la representación del número funciona de manera confiable, como se señaló en mi respuesta original. En particular, std::isnan
y la verificación propuesta a menudo v != v
, no funciona de manera confiable y no debe usarse, para que su código deje de funcionar correctamente cuando alguien decida que se necesita optimización de punto flotante y le pide al compilador que lo haga. Esta situación puede cambiar, los compiladores pueden ser más conformes, pero para este problema que no ha sucedido en los 6 años desde la respuesta original.
Durante unos 6 años, mi respuesta original fue la solución seleccionada para esta pregunta, que estaba bien. Pero recientemente v != v
se ha seleccionado una respuesta altamente votada que recomienda la prueba poco confiable . De ahí esta respuesta adicional más actualizada (ahora tenemos los estándares C ++ 11 y C ++ 14, y C ++ 17 en el horizonte).
Las principales formas de verificar la NaN-ness, a partir de C ++ 14, son:
std::isnan(value) )
es la forma de biblioteca estándar prevista desde C ++ 11. isnan
aparentemente entra en conflicto con la macro Posix del mismo nombre, pero en la práctica eso no es un problema. El problema principal es que cuando se solicita la optimización aritmética de punto flotante, con al menos un compilador principal, a saber, g ++,std::isnan
devuelve el false
argumento NaN .
(fpclassify(value) == FP_NAN) )
Sufre el mismo problema que std::isnan
, es decir, no es confiable.
(value != value) )
Recomendado en muchas respuestas SO. Sufre del mismo problema questd::isnan
, es decir, no es confiable.
(value == Fp_info::quiet_NaN()) )
Esta es una prueba que con el comportamiento estándar no debería detectar NaNs, pero que con el comportamiento optimizado podría detectar NaNs (debido a que el código optimizado solo compara las representaciones de nivel de bit directamente), y tal vez combinado con otra forma de cubrir el comportamiento no optimizado estándar , podría detectar NaN de manera confiable. Desafortunadamente resultó no funcionar de manera confiable.
(ilogb(value) == FP_ILOGBNAN) )
Sufre del mismo problema que std::isnan
, es decir, no es confiable.
isunordered(1.2345, value) )
Sufre del mismo problema que std::isnan
, es decir, no es confiable.
is_ieee754_nan( value ) )
Esta no es una función estándar. Está comprobando los bits de acuerdo con el estándar IEEE 754. Es completamente confiable pero el código depende de alguna manera del sistema.
En el siguiente código de prueba completo, "éxito" es si una expresión informa Nanness del valor. Para la mayoría de las expresiones, esta medida de éxito, el objetivo de detectar NaNs y solo NaNs, corresponde a su semántica estándar. Para el(value == Fp_info::quiet_NaN()) )
Sin embargo, expresión, el comportamiento estándar es que no funciona como un 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 con g ++ (tenga en cuenta de nuevo que el comportamiento estándar de (value == Fp_info::quiet_NaN())
es que no funciona como un detector de NaN, solo es de gran interés práctico aquí):
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> g ++ --version | encontrar "++"
g ++ (x86_64-win32-sjlj-rev1, construido por el proyecto MinGW-W64) 6.3.0
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> g ++ foo.cpp && a
El compilador afirma IEEE 754 = verdadero
v = nan, (std :: isnan (valor)) = verdadero éxito
u = 3.14, (std :: isnan (valor)) = falso Éxito
w = inf, (std :: isnan (valor)) = falso Éxito
v = nan, ((fpclassify (value) == 0x0100)) = verdadero Éxito
u = 3.14, ((fpclassify (value) == 0x0100)) = falso Éxito
w = inf, ((fpclassify (value) == 0x0100)) = falso Éxito
v = nan, ((valor! = valor)) = verdadero Éxito
u = 3.14, ((valor! = valor)) = falso Éxito
w = inf, ((valor! = valor)) = falso Éxito
v = nan, ((valor == Fp_info :: quiet_NaN ())) = falso FALLIDO
u = 3.14, ((valor == Fp_info :: quiet_NaN ())) = falso Éxito
w = inf, ((valor == Fp_info :: quiet_NaN ())) = falso Éxito
v = nan, ((ilogb (value) == ((int) 0x80000000))) = verdadero éxito
u = 3.14, ((ilogb (valor) == ((int) 0x80000000))) = falso Éxito
w = inf, ((ilogb (value) == ((int) 0x80000000))) = falso Éxito
v = nan, (no está ordenado (1.2345, valor)) = verdadero Éxito
u = 3.14, (no está ordenado (1.2345, valor)) = falso Éxito
w = inf, (no está ordenado (1.2345, valor)) = falso Éxito
v = nan, (is_ieee754_nan (value)) = verdadero Éxito
u = 3.14, (is_ieee754_nan (valor)) = falso Éxito
w = inf, (is_ieee754_nan (valor)) = falso Éxito
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> g ++ foo.cpp -ffast-math && a
El compilador afirma IEEE 754 = verdadero
v = nan, (std :: isnan (valor)) = falso FALLIDO
u = 3.14, (std :: isnan (valor)) = falso Éxito
w = inf, (std :: isnan (valor)) = falso Éxito
v = nan, ((fpclassify (value) == 0x0100)) = falso FALLIDO
u = 3.14, ((fpclassify (value) == 0x0100)) = falso Éxito
w = inf, ((fpclassify (value) == 0x0100)) = falso Éxito
v = nan, ((valor! = valor)) = falso FALLIDO
u = 3.14, ((valor! = valor)) = falso Éxito
w = inf, ((valor! = valor)) = falso Éxito
v = nan, ((valor == Fp_info :: quiet_NaN ())) = verdadero Éxito
u = 3.14, ((valor == Fp_info :: quiet_NaN ())) = verdadero FALLIDO
w = inf, ((valor == Fp_info :: quiet_NaN ())) = verdadero FALLIDO
v = nan, ((ilogb (value) == ((int) 0x80000000))) = verdadero éxito
u = 3.14, ((ilogb (valor) == ((int) 0x80000000))) = falso Éxito
w = inf, ((ilogb (value) == ((int) 0x80000000))) = falso Éxito
v = nan, (no está ordenado (1.2345, valor)) = falso FALLIDO
u = 3.14, (no está ordenado (1.2345, valor)) = falso Éxito
w = inf, (no está ordenado (1.2345, valor)) = falso Éxito
v = nan, (is_ieee754_nan (value)) = verdadero Éxito
u = 3.14, (is_ieee754_nan (valor)) = falso Éxito
w = inf, (is_ieee754_nan (valor)) = falso Éxito
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> _
Resultados con Visual C ++:
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> cl / nologo- 2> y 1 | encontrar "++"
Microsoft (R) C / C ++ Optimizing Compiler versión 19.00.23725 para x86
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> cl foo.cpp / Feb && b
foo.cpp
El compilador afirma IEEE 754 = verdadero
v = nan, (std :: isnan (valor)) = verdadero éxito
u = 3.14, (std :: isnan (valor)) = falso Éxito
w = inf, (std :: isnan (valor)) = falso Éxito
v = nan, ((fpclassify (value) == 2)) = verdadero éxito
u = 3.14, ((fpclassify (value) == 2)) = falso Éxito
w = inf, ((fpclassify (value) == 2)) = falso Éxito
v = nan, ((valor! = valor)) = verdadero Éxito
u = 3.14, ((valor! = valor)) = falso Éxito
w = inf, ((valor! = valor)) = falso Éxito
v = nan, ((valor == Fp_info :: quiet_NaN ())) = falso FALLIDO
u = 3.14, ((valor == Fp_info :: quiet_NaN ())) = falso Éxito
w = inf, ((valor == Fp_info :: quiet_NaN ())) = falso Éxito
v = nan, ((ilogb (value) == 0x7fffffff)) = verdadero Éxito
u = 3.14, ((ilogb (valor) == 0x7fffffff)) = falso Éxito
w = inf, ((ilogb (valor) == 0x7fffffff)) = verdadero FALLIDO
v = nan, (no está ordenado (1.2345, valor)) = verdadero Éxito
u = 3.14, (no está ordenado (1.2345, valor)) = falso Éxito
w = inf, (no está ordenado (1.2345, valor)) = falso Éxito
v = nan, (is_ieee754_nan (value)) = verdadero Éxito
u = 3.14, (is_ieee754_nan (valor)) = falso Éxito
w = inf, (is_ieee754_nan (valor)) = falso Éxito
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> cl foo.cpp / Feb / fp: rápido && b
foo.cpp
El compilador afirma IEEE 754 = verdadero
v = nan, (std :: isnan (valor)) = verdadero éxito
u = 3.14, (std :: isnan (valor)) = falso Éxito
w = inf, (std :: isnan (valor)) = falso Éxito
v = nan, ((fpclassify (value) == 2)) = verdadero éxito
u = 3.14, ((fpclassify (value) == 2)) = falso Éxito
w = inf, ((fpclassify (value) == 2)) = falso Éxito
v = nan, ((valor! = valor)) = verdadero Éxito
u = 3.14, ((valor! = valor)) = falso Éxito
w = inf, ((valor! = valor)) = falso Éxito
v = nan, ((valor == Fp_info :: quiet_NaN ())) = falso FALLIDO
u = 3.14, ((valor == Fp_info :: quiet_NaN ())) = falso Éxito
w = inf, ((valor == Fp_info :: quiet_NaN ())) = falso Éxito
v = nan, ((ilogb (value) == 0x7fffffff)) = verdadero Éxito
u = 3.14, ((ilogb (valor) == 0x7fffffff)) = falso Éxito
w = inf, ((ilogb (valor) == 0x7fffffff)) = verdadero FALLIDO
v = nan, (no está ordenado (1.2345, valor)) = verdadero Éxito
u = 3.14, (no está ordenado (1.2345, valor)) = falso Éxito
w = inf, (no está ordenado (1.2345, valor)) = falso Éxito
v = nan, (is_ieee754_nan (value)) = verdadero Éxito
u = 3.14, (is_ieee754_nan (valor)) = falso Éxito
w = inf, (is_ieee754_nan (valor)) = falso Éxito
[C: \ my \ forum \ so \ 282 (detectar NaN)]
> _
Resumiendo los resultados anteriores, solo las pruebas directas de la representación a nivel de bit, utilizando la is_ieee754_nan
función definida en este programa de prueba, funcionaron de manera confiable en todos los casos tanto con g ++ como con Visual C ++.
Anexo:
Después de publicar lo anterior, me di cuenta de otra posible prueba para NaN, mencionada en otra respuesta aquí, a saber ((value < 0) == (value >= 0))
. Resultó funcionar bien con Visual C ++ pero falló con la -ffast-math
opción de g ++ . Solo las pruebas directas de patrones de bits funcionan de manera confiable.