Comment utiliser nan et inf en C?

J’ai une méthode numérique qui pourrait renvoyer nan ou inf s’il y avait une erreur, et pour tester, je voudrais la forcer temporairement à retourner nan ou inf pour s’assurer que la situation est correctement gérée. Existe-t-il un moyen fiable, indépendant du compilateur , de créer des valeurs de nan et d’inf en C?

Après avoir googlé pendant environ 10 minutes, je n’ai pu trouver que des solutions dépendant du compilateur.

Vous pouvez tester si votre implémentation l’a:

#include  #ifdef NAN /* NAN is supported */ #endif #ifdef INFINITY /* INFINITY is supported */ #endif 

L’existence d’ INFINITY est garantie par C99 (ou le dernier brouillon au moins), et se développe en une expression constante de type float représentant l’infini positif ou non signé, si disponible, sinon une constante positive de type float qui déborde au moment de la traduction. ”

NAN peut être défini ou non et “est défini si et seulement si l’implémentation prend en charge des NaN silencieux pour le type float. Elle se développe en une expression constante de type float représentant un NaN silencieux.”

Notez que si vous comparez des valeurs en virgule flottante et que vous faites:

 a = NAN; 

même à ce moment là,

 a == NAN; 

c’est faux. Une façon de vérifier NaN serait:

 #include  if (isnan(a)) { ... } 

Vous pouvez également faire: a != a pour tester si a est NaN.

Il existe également des isfinite() , isinf() , isnormal() et signbit() dans math.h en C99.

C99 a également des fonctions nan :

 #include  double nan(const char *tagp); float nanf(const char *tagp); long double nanl(ocnst char *tagp); 

(Référence: n1256).

Il n’y a pas de méthode indépendante du compilateur, car ni les standards C (ni C ++) ne disent que les types de calcul en virgule flottante doivent prendre en charge NAN ou INF.

Edit: Je viens de vérifier le libellé du standard C ++, et il est dit que ces fonctions (membres de la classe numeric_limits):

 quiet_NaN() signalling_NaN() 

Nous retournerons les représentations NAN “si disponibles”. Il ne développe pas ce que signifie “si disponible”, mais probablement quelque chose comme “si le représentant FP de la mise en œuvre les soutient”. De même, il y a une fonction:

 infinity() 

qui renvoie un rapport INF positif “si disponible”.

Celles-ci sont toutes deux définies dans l’en-tête – je suppose que la norme C a quelque chose de similaire (probablement aussi “si disponible”) mais je n’ai pas de copie de la norme C99 actuelle.

Une manière indépendante du compilateur, mais pas de manière indépendante du processeur pour les obtenir:

 int inf = 0x7F800000; return *(float*)&inf; int nan = 0x7F800001; return *(float*)&nan; 

Cela devrait fonctionner sur tout processeur utilisant le format IEEE 754 (dont x86 fait).

MISE À JOUR: Testé et mis à jour.

Cela fonctionne à la fois pour float et double :

 double NAN = 0.0/0.0; double POS_INF = 1.0 /0.0; double NEG_INF = -1.0/0.0; 

Edit: Comme quelqu’un l’a déjà dit, l’ancienne norme de l’IEEE a déclaré que de telles valeurs devraient susciter des pièges. Mais les nouveaux compilateurs éteignent presque toujours les interruptions et renvoient les valeurs données, car le recouvrement interfère avec la gestion des erreurs.

 double a_nan = strtod("NaN", NULL); double a_inf = strtod("Inf", NULL); 
  /* IEEE positive infinity. */ #if __GNUC_PREREQ(3,3) # define INFINITY (__builtin_inff()) #else # define INFINITY HUGE_VALF #endif 

et

  #ifndef _MATH_H # error "Never use  directly; include  instead." #endif /* IEEE Not A Number. */ #if __GNUC_PREREQ(3,3) # define NAN (__builtin_nanf ("")) #elif defined __GNUC__ # define NAN \ (__extension__ \ ((union { unsigned __l __atsortingbute__ ((__mode__ (__SI__))); float __d; }) \ { __l: 0x7fc00000UL }).__d) #else # include  # if __BYTE_ORDER == __BIG_ENDIAN # define __nan_bytes { 0x7f, 0xc0, 0, 0 } # endif # if __BYTE_ORDER == __LITTLE_ENDIAN # define __nan_bytes { 0, 0, 0xc0, 0x7f } # endif static union { unsigned char __c[4]; float __d; } __nan_union __atsortingbute_used__ = { __nan_bytes }; # define NAN (__nan_union.__d) #endif /* GCC. */ 

Je suis également surpris que ce ne sont pas des constantes de temps de compilation. Mais je suppose que vous pouvez créer ces valeurs assez facilement en exécutant simplement une instruction qui renvoie un résultat non valide. Division par 0, journal de 0, bronzage de 90, ce genre de chose.

J’utilise habituellement

 #define INFINITY (1e999) 

ou

 const double INFINITY = 1e999 

qui fonctionne au moins dans les contextes IEEE 754 car la valeur double représentable la plus élevée est approximativement 1e308 . 1e309 fonctionnerait aussi bien que 1e99999 , mais trois neuf suffisent et sont mémorables. Comme il s’agit d’un littéral double (dans le cas #define ) ou d’une valeur Inf réelle, il restra infini même si vous utilisez des flottants 128 bits («long double»).