Existe-t-il une fonction isnan ()?
PS: Je suis dans MinGW (si cela fait une différence).
J'ai résolu ce problème en utilisant isnan () à partir de <math.h>
, qui n'existe pas dans <cmath>
, ce que j'étais au début #include
ing.
Selon le standard IEEE, les valeurs NaN ont la propriété impaire que les comparaisons les impliquant soient always false. C'est-à-dire que pour un float f, f != f
sera vrai seulement si f est NaN.
Notez que, comme l'ont souligné certains commentaires ci-dessous, tous les compilateurs ne respectent pas cela lors de l'optimisation du code.
Pour tout compilateur qui prétend utiliser la virgule flottante IEEE, cette astuce devrait fonctionner. Mais je ne peux pas garantir que cela fonctionnera dans la pratique. Vérifiez auprès de votre compilateur, en cas de doute.
Il n'y a pas de fonction isnan()
disponible dans la bibliothèque standard C++ actuelle. Il a été introduit dans C99 et défini comme un macro et non une fonction. Les éléments de la bibliothèque de normes définis par C99 ne font pas partie de la norme C++ actuelle ISO/IEC 14882: 1998, ni de sa mise à jour ISO/IEC 14882: 2003.
En 2005, le rapport technique 1 était proposé. Le TR1 apporte la compatibilité avec C99 à C++. Bien qu’il n’ait jamais été officiellement adopté pour devenir la norme C++, de nombreuses implémentations ( GCC 4.0+ ou Visual C++ 9.0+ C++ fournissent des fonctionnalités TR1, toutes ou seulement certaines (Visual C++ 9.0 ne fournit pas de fonctions mathématiques C99).
Si TR1 est disponible, cmath
inclut des éléments C99 tels que isnan()
, isfinite()
, etc., mais ils sont définis en tant que fonctions, et non en macros, généralement dans l'espace de noms std::tr1::
, bien que de nombreuses implémentations (par exemple, GCC 4+ sous Linux ou XCode sous Mac OS X 10.5). +) les injecter directement dans std::
, donc std::isnan
est bien défini.
De plus, certaines implémentations de C++ rendent toujours la macro C99 isnan()
disponible pour C++ (incluse via cmath
ou math.h
), ce qui peut causer davantage de confusion et les développeurs peuvent supposer qu'il s'agit d'un comportement standard.
Remarque concernant Viusal C++, comme mentionné ci-dessus, il ne fournit ni std::isnan
, ni std::tr1::isnan
, mais une fonction d'extension définie en tant que _isnan()
, disponible depuis Visual C++ 6.0 .
Sur XCode, il y a encore plus de plaisir. Comme mentionné, GCC 4+ définit std::isnan
. Pour les anciennes versions du compilateur et de la bibliothèque sous la forme XCode, il semble que (ici discussion pertinente ), je n'ai pas eu la chance de me vérifier), deux fonctions sont définies, __inline_isnand()
sur Intel et __isnand()
sur Power PC.
Depuis que cette question a été posée, il y a eu quelques nouveaux développements: il est important de savoir que std::isnan()
fait partie de C++ 11
Défini dans l'en-tête <cmath>
bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)
Détermine si le nombre en virgule flottante donné n'est pas un nombre (NaN
).
Paramètres
arg
: valeur en virgule flottante
Valeur de retour
true
si arg est NaN
, false
sinon
Référence
http://fr.cppreference.com/w/cpp/numeric/math/isnan
Veuillez noter que ceci est incompatible avec -fast-math si vous utilisez g ++, voir ci-dessous pour d'autres suggestions.
Pour C99, en C, cela est implémenté sous la forme d'une macro isnan(c)
qui renvoie une valeur int. Le type de x
doit être float, double ou long double.
Différents fournisseurs peuvent inclure ou non une fonction isnan()
.
Le moyen supposé portable de vérifier NaN
consiste à utiliser la propriété IEEE 754 qui NaN
n'est pas égale à elle-même: i.e. x == x
sera faux pour que x
soit NaN
.
Cependant, la dernière option peut ne pas fonctionner avec tous les compilateurs et certains paramètres (en particulier les paramètres d'optimisation), vous pouvez donc toujours vérifier le modèle de bits ...
Il y a aussi une bibliothèque header-only présente dans Boost qui dispose d'outils pratiques pour gérer les types de données à virgule flottante.
#include <boost/math/special_functions/fpclassify.hpp>
Vous obtenez les fonctions suivantes:
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);
Si vous avez le temps, jetez un coup d'œil à l'ensemble de la boîte à outils Math de Boost, qui contient de nombreux outils utiles et qui se développe rapidement.
De même, lorsqu'il s'agit de points flottants et non flottants, il peut être judicieux de regarder les conversions numériques .
Il existe trois méthodes "officielles": posixisnan
macro , c ++ 0xisnan
modèle de fonction ou visuel c ++_isnan
fonction .
Malheureusement, il est plutôt difficile de déterminer lequel de ceux-ci utiliser.
Et malheureusement, il n’existe aucun moyen fiable de détecter si vous avez une représentation IEEE 754 avec des NaN. La bibliothèque standard offre un tel moyen officiel (numeric_limits<double>::is_iec559
). Mais dans la pratique, des compilateurs tels que g ++ gâchent tout cela.
En théorie, on pourrait utiliser simplementx != x
, mais des compilateurs tels que g ++ et visual c ++ feraient des siennes.
Donc, à la fin, testez le NaN bitpatterns spécifique, en supposant (et éventuellement en imposant, à un moment donné!) Une représentation particulière telle que IEEE 754.
EDIT: comme exemple de "compilateurs tels que g ++… fuck that up", considérons
#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 );
}
Compiler avec g ++ (TDM-2 mingw32) 4.4.1:
C:\test> tapez "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 fonctionne ... | | echo! failed fonctionne ... C:\test> gnuc x.cpp --fast-math C:\test> a && echo fonctionne ... || echo! a échoué L'assertion a échoué: a! = b, fichier x.cpp, ligne 6 Cette application a demandé au Runtime de le terminer de manière inhabituelle . Veuillez contacter l'équipe de support de l'application. pour plus d'informations . ! a échoué C:\test> _
Il y a un std :: isnan si votre compilateur supporte les extensions c99, mais je ne sais pas si mingw le fait.
Voici une petite fonction qui devrait fonctionner si votre compilateur n’a pas la fonction standard:
bool custom_isnan(double var)
{
volatile double d = var;
return d != d;
}
Vous pouvez utiliser numeric_limits<float>::quiet_NaN( )
défini dans la bibliothèque standard limits
à tester. Il existe une constante distincte définie pour 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;
}
Je ne sais pas si cela fonctionne sur toutes les plates-formes, car je n'ai testé qu'avec g ++ sous Linux.
Vous pouvez utiliser la fonction isnan()
, mais vous devez inclure la bibliothèque de mathématiques C.
#include <cmath>
Comme cette fonction fait partie de C99, elle n’est pas disponible partout. Si votre fournisseur ne fournit pas la fonction, vous pouvez également définir votre propre variante pour la compatibilité.
inline bool isnan(double x) {
return x != x;
}
Ma réponse à cette question est n'utilisez pas de contrôles rétroactifs pour nan
. Utiliser préventif recherche à la place les divisions de la forme 0.0/0.0
.
#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.
nan
résulte de l'opération 0.f/0.f
ou 0.0/0.0
. nan
est une terrible némésis pour la stabilité de votre code qui doit être détecté et empêché très soigneusement1. Les propriétés de nan
qui diffèrent des nombres normaux:
nan
est toxique, (5 * nan
= nan
)nan
n'est égal à rien, pas même à lui-même (nan
! = nan
)nan
pas plus grand que tout (nan
!> 0)nan
n'est pas moins que tout (nan
! <0)Les 2 dernières propriétés répertoriées sont contre-logiques et donneront lieu à un comportement étrange du code reposant sur des comparaisons avec un nombre nan
(la 3ème dernière propriété est également étrange, mais vous ne verrez probablement jamais x != x ?
dans votre code (sauf si vous sont à la recherche de nan (non fiable))).
Dans mon propre code, j'ai remarqué que les valeurs nan
ont tendance à produire des bogues difficiles à trouver. (Notez comment cela est not le cas de inf
ou -inf
. (-inf
<0) renvoie TRUE
, (0 <inf
) renvoie VRAI, et même (-inf
<inf
) renvoie VRAI. Donc, selon mon expérience , le comportement du code est souvent toujours comme vous le souhaitez).
Ce que vous voulez voir sous 0.0/0.0
doit être traité comme un cas spécial, mais ce que vous faites doit dépendre des nombres que vous comptez extraire du code.
Dans l'exemple ci-dessus, le résultat de (0.f/FLT_MIN
) sera essentiellement 0
. Vous voudrez peut-être que 0.0/0.0
génère plutôt HUGE
. Alors,
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.
Donc dans ce qui précède, si x était 0.f
, il en résulterait inf
(qui a en fait un comportement plutôt bon/non destructif, comme mentionné ci-dessus).
N'oubliez pas que la division entière par 0 provoque une exception d'exécution . Vous devez donc toujours vérifier la division de nombre entier par 0. Ce n'est pas parce que 0.0/0.0
est évalué à nan
que vous pouvez être paresseux et ne pas vérifier avant que 0.0/0.0
ne se produise.
1 Les vérifications de nan
via x != x
ne sont parfois pas fiables (x != x
étant supprimé par certains compilateurs optimalisants qui ne respectent pas la conformité IEEE, en particulier lorsque le commutateur -ffast-math
est activé).
Le code suivant utilise la définition de NAN (tous les bits d'exposant définis, au moins un ensemble de bits fractionnaire) et suppose que sizeof (int) = sizeof (float) = 4. Vous pouvez rechercher NAN dans Wikipedia pour plus de détails.
bool IsNan( float value )
{
return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000;
}
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);
}
Cela fonctionne si sizeof(int)
est 4 et sizeof(long long)
est 8.
Pendant la phase d'exécution, ce n'est qu'une comparaison, les castings ne prennent pas de temps. Il modifie simplement la configuration des indicateurs de comparaison pour vérifier l'égalité.
Après avoir lu les autres réponses, je voulais quelque chose qui passerait par l’avertissement de comparaison en virgule flottante et qui ne casserait pas les calculs rapides. Le code suivant semble fonctionner:
/*
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;
}
Pour moi, la solution pourrait être une macro pour la rendre explicitement en ligne et donc assez rapide . Cela fonctionne également pour tout type de type float. Il se base sur le fait que le seul cas où une valeur n'est pas égale à elle-même est lorsque la valeur n'est pas un nombre.
#ifndef isnan
#define isnan(a) (a != a)
#endif
Une solution possible qui ne dépendrait pas de la représentation IEEE spécifique pour NaN utilisée serait la suivante:
template<class T>
bool isnan( T f ) {
T _nan = (T)0.0/(T)0.0;
return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}
Considérant que (x! = X) n'est pas toujours garanti pour NaN (par exemple, si vous utilisez l'option -ffast-math), j'ai utilisé:
#define IS_NAN(x) (((x) < 0) == ((x) >= 0))
Les nombres ne peuvent pas être à la fois <0 et> = 0; cette vérification ne réussit donc que si le nombre n'est ni inférieur ni supérieur à zéro. Ce qui est fondamentalement pas de nombre du tout, ou NaN.
Vous pouvez également utiliser ceci si vous préférez:
#define IS_NAN(x) (!((x)<0) && !((x)>=0)
Cependant, je ne sais pas comment cela est affecté par -ffast-math, votre kilométrage peut donc varier.
Cela marche:
#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;
}
sortie: isnan
Il me semble que la meilleure approche véritablement multi-plateforme serait d'utiliser une union et de tester le motif de bits du double pour vérifier les NaN.
Je n’ai pas testé cette solution à fond et il existe peut-être un moyen plus efficace de travailler avec les modèles de bits, mais je pense que cela devrait fonctionner.
#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;
}
La norme IEEE dit Lorsque l'exposant est égal à 1 et Mantissa n'est pas égal à zéro, Le nombre est un NaN .. Double est égal à 1 bit de signe, 11 bits d'exposant et 52 bits de mantisse. Faites une petite vérification.
Cela détecte l'infini ainsi que NaN dans Visual Studio en vérifiant qu'il est dans les limites doubles:
//#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;