Depuis C ++ 14, il existe plusieurs façons de tester si un nombre à virgule flottante value
est un NaN.
De ces façons, seule la vérification des bits de la représentation du nombre fonctionne de manière fiable, comme indiqué dans ma réponse d'origine. En particulier, std::isnan
et la vérification souvent proposée v != v
, ne fonctionne pas de manière fiable et ne doit pas être utilisée, de peur que votre code cesse de fonctionner correctement lorsque quelqu'un décide que l'optimisation en virgule flottante est nécessaire et demande au compilateur de le faire. Cette situation peut changer, les compilateurs peuvent devenir plus conformes, mais pour ce problème qui ne s'est pas produit au cours des 6 années écoulées depuis la réponse d'origine.
Pendant environ 6 ans, ma réponse initiale était la solution choisie pour cette question, qui était OK. Mais récemment, une réponse très appréciée recommandant le v != v
test non fiable a été sélectionnée. D'où cette réponse encore plus à jour (nous avons désormais les standards C ++ 11 et C ++ 14, et C ++ 17 à l'horizon).
Les principaux moyens de vérifier la non-NaN, à partir de C ++ 14, sont les suivants:
std::isnan(value) )
est le moyen de bibliothèque standard prévu depuis C ++ 11. isnan
apparemment en conflit avec la macro Posix du même nom, mais en pratique ce n'est pas un problème. Le problème principal est que lorsque l'optimisation arithmétique à virgule flottante est demandée, alors avec au moins un compilateur principal, à savoir g ++, std::isnan
renvoie l' false
argument NaN .
(fpclassify(value) == FP_NAN) )
Souffre du même problème std::isnan
, c'est-à-dire n'est pas fiable.
(value != value) )
Recommandé dans de nombreuses réponses SO. Souffre du même problème std::isnan
, c'est-à-dire n'est pas fiable.
(value == Fp_info::quiet_NaN()) )
Il s'agit d'un test qui, avec un comportement standard, ne devrait pas détecter les NaN, mais qui, avec le comportement optimisé, pourrait détecter les NaN (en raison du code optimisé comparant directement les représentations de niveau de bit), et peut-être combiné avec une autre façon de couvrir le comportement standard non optimisé , pourrait détecter de manière fiable NaN. Malheureusement, il s'est avéré ne pas fonctionner de manière fiable.
(ilogb(value) == FP_ILOGBNAN) )
Souffre du même problème std::isnan
, c'est-à-dire n'est pas fiable.
isunordered(1.2345, value) )
Souffre du même problème std::isnan
, c'est-à-dire n'est pas fiable.
is_ieee754_nan( value ) )
Ce n'est pas une fonction standard. C'est la vérification des bits selon la norme IEEE 754. Il est complètement fiable mais le code dépend quelque peu du système.
Dans le code de test complet suivant, «succès» consiste à savoir si une expression signale Nan-ness de la valeur. Pour la plupart des expressions, cette mesure de succès, l'objectif de détecter les NaN et uniquement les NaN, correspond à leur sémantique standard. Pour l' (value == Fp_info::quiet_NaN()) )
expression, cependant, le comportement standard est qu'il ne fonctionne pas comme un détecteur 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 ) );
}
Résultats avec g ++ (notez à nouveau que le comportement standard de (value == Fp_info::quiet_NaN())
est qu'il ne fonctionne pas comme un détecteur NaN, c'est juste très intéressant ici):
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> g ++ --version | trouver "++"
g ++ (x86_64-win32-sjlj-rev1, construit par le projet MinGW-W64) 6.3.0
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> g ++ foo.cpp && a
Le compilateur revendique IEEE 754 = true
v = nan, (std :: isnan (valeur)) = vrai succès
u = 3,14, (std :: isnan (valeur)) = faux succès
w = inf, (std :: isnan (value)) = false Réussite
v = nan, ((fpclassify (value) == 0x0100)) = true Success
u = 3,14, ((fpclassify (value) == 0x0100)) = false Success
w = inf, ((fpclassify (value) == 0x0100)) = false Success
v = nan, ((valeur! = valeur)) = vrai succès
u = 3,14, ((valeur! = valeur)) = faux succès
w = inf, ((valeur! = valeur)) = faux Succès
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 (valeur) == ((int) 0x80000000))) = vrai succès
u = 3,14, ((ilogb (valeur) == ((int) 0x80000000))) = faux succès
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)) = vrai succès
u = 3,14, (is_ieee754_nan (valeur)) = faux succès
w = inf, (is_ieee754_nan (value)) = false Success
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> g ++ foo.cpp -fast-math && a
Le compilateur revendique IEEE 754 = true
v = nan, (std :: isnan (valeur)) = false ÉCHEC
u = 3,14, (std :: isnan (valeur)) = faux succès
w = inf, (std :: isnan (value)) = false Réussite
v = nan, ((fpclassify (value) == 0x0100)) = false FAILED
u = 3,14, ((fpclassify (value) == 0x0100)) = false Success
w = inf, ((fpclassify (value) == 0x0100)) = false Success
v = nan, ((valeur! = valeur)) = faux ÉCHEC
u = 3,14, ((valeur! = valeur)) = faux succès
w = inf, ((valeur! = valeur)) = faux Succès
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 (valeur) == ((int) 0x80000000))) = vrai succès
u = 3,14, ((ilogb (valeur) == ((int) 0x80000000))) = faux succès
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)) = vrai succès
u = 3,14, (is_ieee754_nan (valeur)) = faux succès
w = inf, (is_ieee754_nan (value)) = false Success
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> _
Résultats avec Visual C ++:
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> cl / nologo- 2> & 1 | trouver "++"
Microsoft (R) C / C ++ Optimizing Compiler Version 19.00.23725 pour x86
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> cl foo.cpp / Feb && b
foo.cpp
Le compilateur revendique IEEE 754 = true
v = nan, (std :: isnan (valeur)) = vrai succès
u = 3,14, (std :: isnan (valeur)) = faux succès
w = inf, (std :: isnan (value)) = false Réussite
v = nan, ((fpclassify (value) == 2)) = true Success
u = 3,14, ((fpclassify (value) == 2)) = false Success
w = inf, ((fpclassify (value) == 2)) = false Success
v = nan, ((valeur! = valeur)) = vrai succès
u = 3,14, ((valeur! = valeur)) = faux succès
w = inf, ((valeur! = valeur)) = faux Succès
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 (valeur) == 0x7fffffff)) = vrai succès
u = 3,14, ((ilogb (valeur) == 0x7fffffff)) = faux succès
w = inf, ((ilogb (valeur) == 0x7fffffff)) = true ÉCHEC
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)) = vrai succès
u = 3,14, (is_ieee754_nan (valeur)) = faux succès
w = inf, (is_ieee754_nan (value)) = false Success
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> cl foo.cpp / Feb / fp: rapide && b
foo.cpp
Le compilateur revendique IEEE 754 = true
v = nan, (std :: isnan (valeur)) = vrai succès
u = 3,14, (std :: isnan (valeur)) = faux succès
w = inf, (std :: isnan (value)) = false Réussite
v = nan, ((fpclassify (value) == 2)) = true Success
u = 3,14, ((fpclassify (value) == 2)) = false Success
w = inf, ((fpclassify (value) == 2)) = false Success
v = nan, ((valeur! = valeur)) = vrai succès
u = 3,14, ((valeur! = valeur)) = faux succès
w = inf, ((valeur! = valeur)) = faux Succès
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 (valeur) == 0x7fffffff)) = vrai succès
u = 3,14, ((ilogb (valeur) == 0x7fffffff)) = faux succès
w = inf, ((ilogb (valeur) == 0x7fffffff)) = true ÉCHEC
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)) = vrai succès
u = 3,14, (is_ieee754_nan (valeur)) = faux succès
w = inf, (is_ieee754_nan (value)) = false Success
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> _
Pour résumer les résultats ci-dessus, seuls les tests directs de la représentation au niveau du bit, en utilisant la is_ieee754_nan
fonction définie dans ce programme de test, ont fonctionné de manière fiable dans tous les cas avec g ++ et Visual C ++.
Addendum:
Après avoir publié ce qui précède, j'ai pris connaissance d'un autre test possible pour NaN, mentionné dans une autre réponse ici, à savoir ((value < 0) == (value >= 0))
. Cela s'est avéré fonctionner correctement avec Visual C ++ mais a échoué avec l' -ffast-math
option de g ++ . Seuls les tests Bitpattern directs fonctionnent de manière fiable.