J'utilisais des fonctions isinf
, isnan
sur des plates-formes Linux qui fonctionnaient parfaitement . Mais cela ne fonctionnait pas sous OS-X, j'ai donc décidé d'utiliser std::isinf
std::isnan
, qui fonctionne à la fois sous Linux et OS-X.
Mais le compilateur Intel ne le reconnaît pas et je suppose que c'est un bogue du compilateur Intel selon http://software.intel.com/en-us/forums/showthread.php?t=64188
Alors maintenant, je veux juste éviter les tracas et définir ma propre implémentation isinf
, isnan
.
Est-ce que quelqu'un sait comment cela pourrait être fait?
modifier:
J'ai fini par le faire dans mon code source pour faire fonctionner isinf
/isnan
#include <iostream>
#include <cmath>
#ifdef __INTEL_COMPILER
#include <mathimf.h>
#endif
int isnan_local(double x) {
#ifdef __INTEL_COMPILER
return isnan(x);
#else
return std::isnan(x);
#endif
}
int isinf_local(double x) {
#ifdef __INTEL_COMPILER
return isinf(x);
#else
return std::isinf(x);
#endif
}
int myChk(double a){
std::cerr<<"val is: "<<a <<"\t";
if(isnan_local(a))
std::cerr<<"program says isnan";
if(isinf_local(a))
std::cerr<<"program says isinf";
std::cerr<<"\n";
return 0;
}
int main(){
double a = 0;
myChk(a);
myChk(log(a));
myChk(-log(a));
myChk(0/log(a));
myChk(log(a)/log(a));
return 0;
}
Vous pouvez également utiliser boost pour cette tâche:
#include <boost/math/special_functions/fpclassify.hpp> // isnan
if( boost::math::isnan( ... ) .... )
Je n'ai pas essayé cela, mais je penserais
int isnan(double x) { return x != x; }
int isinf(double x) { return !isnan(x) && isnan(x - x); }
travaillerait. Il semble qu’il devrait exister un meilleur moyen pour isinf, mais cela devrait fonctionner.
Selon this , infinity est facile à vérifier:
NaN est un peu plus compliqué car il n'a pas de représentation unique:
Vous trouverez ci-dessous le code du cas à virgule flottante double précision. La précision simple peut être écrite de la même manière (rappelons que l’exposant est de 11 bits pour les doubles et de 8 bits pour les simples):
int isinf(double x)
{
union { uint64 u; double f; } ieee754;
ieee754.f = x;
return ( (unsigned)(ieee754.u >> 32) & 0x7fffffff ) == 0x7ff00000 &&
( (unsigned)ieee754.u == 0 );
}
int isnan(double x)
{
union { uint64 u; double f; } ieee754;
ieee754.f = x;
return ( (unsigned)(ieee754.u >> 32) & 0x7fffffff ) +
( (unsigned)ieee754.u != 0 ) > 0x7ff00000;
}
L'implémentation est assez simple (j'ai pris ceux des fichiers d'en-tête OpenCV ). Il utilise une union sur un entier 64 bits non signé de taille égale que vous devrez peut-être déclarer correctement:
#if defined _MSC_VER
typedef unsigned __int64 uint64;
#else
typedef uint64_t uint64;
#endif
Cela fonctionne sous Visual Studio 2008:
#include <math.h>
#define isnan(x) _isnan(x)
#define isinf(x) (!_finite(x))
#define fpu_error(x) (isinf(x) || isnan(x))
Pour des raisons de sécurité, je vous recommande d’utiliser fpu_error (). Je crois que certains nombres sont relevés avec isnan () et d’autres avec isinf (), et vous avez besoin des deux pour être sûr.
Voici un code de test:
double zero=0;
double infinite=1/zero;
double proper_number=4;
printf("isinf(infinite)=%d.\n",isinf(infinite));
printf("isinf(proper_number)=%d.\n",isinf(proper_number));
printf("isnan(infinite)=%d.\n",isnan(infinite));
printf("isnan(proper_number)=%d.\n",isnan(proper_number));
double num=-4;
double neg_square_root=sqrt(num);
printf("isinf(neg_square_root)=%d.\n",isinf(neg_square_root));
printf("isinf(proper_number)=%d.\n",isinf(proper_number));
printf("isnan(neg_square_root)=%d.\n",isnan(neg_square_root));
printf("isnan(proper_number)=%d.\n",isnan(proper_number));
Voici la sortie:
isinf(infinite)=1.
isinf(proper_number)=0.
isnan(infinite)=0.
isnan(proper_number)=0.
isinf(neg_square_root)=1.
isinf(proper_number)=0.
isnan(neg_square_root)=1.
isnan(proper_number)=0.
isnan
fait maintenant partie de C++ 11, inclus dans GCC++ je crois, et Apple LLVM.
Maintenant MSVC++ a une fonction _isnan
dans <float.h>
.
#define
s et #include
s appropriés doivent permettre une solution de contournement appropriée.
Cependant, je recommande d'empêcher la survenue de nan au lieu de la détection de nan .
Eh bien, idéalement, vous attendriez qu'Intel corrige le bogue ou fournisse une solution de contournement :-)
Mais si vous voulez détecter les valeurs NaN
et Inf
à partir de valeurs IEEE754, mappez-les sur un entier (32 ou 64 bits, selon qu'il s'agisse d'une simple ou d'une double précision) et vérifiez si les bits d'exposant sont tous 1. Cela indique ces deux cas.
Vous pouvez faire la distinction entre NaN
et Inf
en cochant le bit de poids fort de la mantisse. Si c'est 1, c'est NaN
sinon Inf
.
+/-Inf
est dicté par le bit de signe.
Pour une précision simple (valeurs 32 bits), le signe est le bit de poids fort (b31), l'exposant est les huit bits suivants (plus une mantisse de 23 bits). Pour la double précision, le signe est toujours le bit de poids fort, mais l'exposant est de onze bits (plus 52 bits pour la mantisse).
Wikipedia a tous les détails sanglants.
Le code suivant vous montre comment fonctionne l'encodage.
#include <stdio.h>
static void decode (char *s, double x) {
long y = *(((long*)(&x))+1);
printf("%08x ",y);
if ((y & 0x7ff80000L) == 0x7ff80000L) {
printf ("NaN (%s)\n", s);
return;
}
if ((y & 0xfff10000L) == 0x7ff00000L) {
printf ("+Inf (%s)\n", s);
return;
}
if ((y & 0xfff10000L) == 0xfff00000L) {
printf ("-Inf (%s)\n", s);
return;
}
printf ("%e (%s)\n", x, s);
}
int main (int argc, char *argv[]) {
double dvar;
printf ("sizeof double = %d\n", sizeof(double));
printf ("sizeof long = %d\n", sizeof(long));
dvar = 1.79e308; dvar = dvar * 10000;
decode ("too big", dvar);
dvar = -1.79e308; dvar = dvar * 10000;
decode ("too big and negative", dvar);
dvar = -1.0; dvar = sqrt(dvar);
decode ("imaginary", dvar);
dvar = -1.79e308;
decode ("normal", dvar);
return 0;
}
et il produit:
sizeof double = 8
sizeof long = 4
7ff00000 +Inf (too big)
fff00000 -Inf (too big and negative)
fff80000 NaN (imaginary)
ffefdcf1 -1.790000e+308 (normal)
Gardez juste à l’esprit que ce code (mais pas la méthode) dépend beaucoup de la taille de vos fichiers longs qui n’est pas trop portable. Mais, si vous devez mordiller pour obtenir l'information, vous êtes déjà entré sur ce territoire :-)
En passant, j'ai toujours trouvé le convertisseur IEEE754 de Harald Schmidt très utile pour l'analyse en virgule flottante.
Comme l'a dit Brubelsabs, Boost propose cette fonctionnalité mais, comme indiqué ici , au lieu d'utiliser
if (boost::math::isnan(number))
Cela devrait être utilisé:
if ((boost::math::isnan)(number))
Personne ne semble avoir mentionné la fonction C99 fpclassify qui renvoie:
Un type parmi FP_INFINITE, FP_NAN, FP_NORMAL, FP_SUBNORMAL, FP_ZERO ou défini par l'implémentation, spécifiant la catégorie de arg.
Cela fonctionne avec Visual Studio, mais je ne connais pas OS-X.
Utilisez simplement ce code extrêmement simple conforme à IEEE 754-1985:
static inline bool ISINFINITE( float a ) { return (((U32&) a) & 0x7FFFFFFFU) == 0x7F800000U; }
static inline bool ISINFINITEPOSITIVE( float a ) { return (((U32&) a) & 0xFFFFFFFFU) == 0x7F800000U; }
static inline bool ISINFINITENEGATIVE( float a ) { return (((U32&) a) & 0xFFFFFFFFU) == 0xFF800000U; }
static inline bool ISNAN( float a ) { return !ISINFINITE( a ) && (((U32&) a) & 0x7F800000U) == 0x7F800000U; }
static inline bool ISVALID( float a ) { return (((U32&) a) & 0x7F800000U) != 0x7F800000U; }
L'article suivant présente quelques astuces intéressantes pour isnan et isinf: http://jacksondunstan.com/articles/983