J'ai une méthode numérique qui pourrait renvoyer nan ou inf en cas d'erreur, et pour des raisons de test, j'aimerais la forcer temporairement à renvoyer nan ou inf pour s'assurer que la situation est traitée correctement. Existe-t-il un moyen fiable, indépendant du compilateur de créer des valeurs de nan et inf en C?
Après avoir recherché Google pendant environ 10 minutes, je n’ai pu trouver que des solutions dépendantes du compilateur.
Vous pouvez tester si votre implémentation en est dotée:
#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 positif ou non signé. temps de 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:
a = NAN;
même à ce moment là,
a == NAN;
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 si a
est NaN.
Il existe également des macros isfinite()
, isinf()
, isnormal()
et signbit()
dans math.h
en C99.
C99 a aussi les fonctions nan
:
#include <math.h>
double nan(const char *tagp);
float nanf(const char *tagp);
long double nanl(const char *tagp);
(Référence: n1256).
Il n’existe aucun moyen indépendant du compilateur de le faire, car ni les normes C (ni C++) ne précisent que les types mathématiques à virgule flottante doivent prendre en charge NAN ou INF.
Edit: Je viens de vérifier le libellé de la norme C++ et il indique que ces fonctions (membres de la classe basée sur un modèle numeric_limits):
quiet_NaN()
signalling_NaN()
renverra les représentations NAN "si disponibles". Il ne développe pas ce que "si disponible" signifie, mais probablement quelque chose comme "si le représentant de FP de la mise en œuvre les prend en charge". De même, il existe une fonction:
infinity()
qui renvoie un INF rep positif "si disponible".
Ceux-ci sont tous deux définis dans l'en-tête <limits>
- je suppose que le standard C a quelque chose de similaire (probablement aussi "si disponible") mais je n'ai pas de copie du standard actuel C99.
Cela fonctionne 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 on l’a déjà dit, l’ancienne norme IEEE stipulait que De telles valeurs devraient soulever des pièges. Mais les nouveaux compilateurs Désactivent presque toujours les pièges et renvoient les valeurs Données, car ils interférent avec le traitement des erreurs.
Un moyen indépendant du compilateur, mais non indépendant du processeur, d'obtenir ces éléments:
int inf = 0x7F800000;
return *(float*)&inf;
int nan = 0x7F800001;
return *(float*)&nan;
Cela devrait fonctionner sur tout processeur utilisant le format à virgule flottante IEEE 754 (comme x86).
UPDATE: Testé et mis à jour.
double a_nan = strtod("NaN", NULL);
double a_inf = strtod("Inf", NULL);
<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. */
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. S'agissant d'un double littéral (dans le cas #define
) ou d'une valeur réelle Inf
, il restera infini même si vous utilisez des flottants de 128 bits («long double»).
Je suis également surpris que ces constantes de temps ne soient pas compilées. 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, tan de 90, ce genre de chose.