web-dev-qa-db-fra.com

Vérifier si un double (ou float) est NaN en C++

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

340
hasen

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.

330
jalf

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.

213
mloskot

Première solution: si vous utilisez C++ 11

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

Synopsis

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.


Autres solutions: si vous utilisez des outils non conformes à C++ 11

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

146
BlueTrin

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 .

82
Anonymous

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> _
43

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

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.

25
Bill the Lizard

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

nan prévention

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

que faire sous nan

Ce que vous voulez voir sous 0.0/0.0doit ê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.

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é).

12
bobobobo

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

11
Ian
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é.

7
ST3

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
4
user1705817

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) );
}
4
Dan Nathan

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.

4
Jerramy

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

3
edW

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;
}
1
Sheldon Juncker

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.

0
bop

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;
0
mathengineer