J'ai une méthode numérique qui pourrait retourner nan ou inf s'il y avait une erreur, et pour les tests intentionnels, j'aimerais la forcer temporairement à retourner nan ou inf pour m'assurer que la situation est gérée correctement. Existe-t-il un moyen fiable et indépendant du compilateur de créer des valeurs de nan et inf en C?
Après avoir cherché sur Google pendant environ 10 minutes, je n'ai pu trouver que des solutions dépendantes du compilateur.
Réponses:
Vous pouvez tester si votre implémentation l'a:
#include <math.h> #ifdef NAN /* NAN is supported */ #endif #ifdef INFINITY /* INFINITY is supported */ #endif
L'existence de
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, en une constante positive de type float qui déborde au moment de la traduction."NAN
peut ou non être défini et "est défini si et seulement si l'implémentation prend en charge les NaN silencieux pour le type float. Il se développe en une expression constante de type float représentant un NaN silencieux."Notez que si vous comparez des valeurs à virgule flottante, et faites:
même à ce moment là,
c'est faux. Une façon de vérifier NaN serait:
#include <math.h> if (isnan(a)) { ... }
Vous pouvez également faire:
a != a
pour tester sia
est NaN.Il y a aussi
isfinite()
,isinf()
,isnormal()
etsignbit()
macrosmath.h
dans C99.C99 a également des
nan
fonctions:#include <math.h> double nan(const char *tagp); float nanf(const char *tagp); long double nanl(const char *tagp);
(Référence: n1256).
Docs INFINITY Docs NAN
la source
a != a
ne doit JAMAIS être utilisé.a
est un not-a-number,a == NAN
de renvoyer false. IEEE l'exige. Même les implémentations qui adhèrent à l'IEEE le font principalement . Lorsqu'ilisnan()
n'est pas implémenté, il est préférable d'encapsuler le test que de coder directementa == NAN
.Il n'y a pas de moyen indépendant du compilateur de faire cela, car ni les normes C (ni C ++) ne disent que les types mathématiques à virgule flottante doivent prendre en charge NAN ou INF.
Edit: Je viens de vérifier le libellé du standard C ++, et il dit que ces fonctions (membres de la classe modèle numeric_limits):
renverra 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 l'implémentation les soutient». De même, il existe une fonction:
qui renvoie un représentant INF positif "si disponible".
Ceux-ci sont tous deux définis dans l'en-
<limits>
tête - je suppose que le standard C a quelque chose de similaire (probablement aussi "si disponible") mais je n'ai pas de copie du standard C99 actuel.la source
<math.h>
définitnan()
,nanf()
etnanl()
que le retour des représentations différentes de NaN (commedouble
,float
etint
respectivement), et l' infini (si avaliable) peut être retourné en générant une aveclog(0)
ou quelque chose. Il n'y a pas de moyen standard de les vérifier, même dans C99. L'en-<float.h>
tête (<limits.h>
pour les types intégraux) est malheureusement muet sur les valeursinf
etnan
.nanl()
renvoie along double
, pas unint
comme mon commentaire dit. Je ne sais pas pourquoi je ne m'en suis pas rendu compte quand je l'ai tapé.Cela fonctionne à la fois pour
float
etdouble
: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 IEEE disait que de telles valeurs devraient lever des pièges. Mais les nouveaux compilateurs désactivent presque toujours les traps et renvoient les valeurs données car le trapping interfère avec la gestion des erreurs.
la source
#define is_nan(x) ((x) != (x))
peut être utile comme test simple et portable pour NAN.Un moyen indépendant du compilateur, mais pas un moyen indépendant du processeur pour les obtenir:
int inf = 0x7F800000; return *(float*)&inf; int nan = 0x7F800001; return *(float*)&nan;
Cela devrait fonctionner sur n'importe quel processeur utilisant le format à virgule flottante IEEE 754 (ce que fait x86).
MISE À JOUR: Testé et mis à jour.
la source
(float &)
? Cela ne me ressemble pas à C. Vous avez besoinint i = 0x7F800000; return *(float *)&i;
0x7f800001
c'est ce qu'on appelle un NaN de signalisation dans la norme IEEE-754. Bien que la plupart des bibliothèques et du matériel ne prennent pas en charge la signalisation des NaN, il est probablement préférable de renvoyer un NaN silencieux comme0x7fc00000
.double a_nan = strtod("NaN", NULL); double a_inf = strtod("Inf", NULL);
la source
strtod
et convertit les NaN et les Inf.<inf.h> /* IEEE positive infinity. */ #if __GNUC_PREREQ(3,3) # define INFINITY (__builtin_inff()) #else # define INFINITY HUGE_VALF #endif
et
<bits/nan.h> #ifndef _MATH_H # error "Never use <bits/nan.h> directly; include <math.h> instead." #endif /* IEEE Not A Number. */ #if __GNUC_PREREQ(3,3) # define NAN (__builtin_nanf ("")) #elif defined __GNUC__ # define NAN \ (__extension__ \ ((union { unsigned __l __attribute__ ((__mode__ (__SI__))); float __d; }) \ { __l: 0x7fc00000UL }).__d) #else # include <endian.h> # 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 __attribute_used__ = { __nan_bytes }; # define NAN (__nan_union.__d) #endif /* GCC. */
la source
Je suis également surpris qu'il ne s'agisse pas de 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 aussi invalide. Diviser par 0, log de 0, bronzage de 90, ce genre de chose.
la source
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 à peu près
1e308
.1e309
fonctionnerait aussi bien, comme le ferait1e99999
, mais trois neuf sont suffisants et mémorables. Puisqu'il s'agit d'un double littéral (dans le#define
cas) ou d'uneInf
valeur réelle , il restera infini même si vous utilisez des flottants de 128 bits («long double»).la source
1e999
littéraux ne sont plus arrondis à+Infinity
. Selon les lois de Murphy, cela brise un algorithme. Pire encore: un programmeur humain effectuant la construction "128 bits" ne repérera probablement pas cette erreur à l'avance. C'est-à-dire qu'il sera probablement trop tard lorsque cette erreur sera trouvée et reconnue. Très dangereux.Voici un moyen simple de définir ces constantes, et je suis presque sûr que c'est portable:
const double inf = 1.0/0.0; const double nan = 0.0/0.0;
Quand j'exécute ce code:
printf("inf = %f\n", inf); printf("-inf = %f\n", -inf); printf("nan = %f\n", nan); printf("-nan = %f\n", -nan);
Je reçois:
la source