web-dev-qa-db-fra.com

comment créer une fonction isnan/isinf portable

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::isinfstd::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;
}
37
monkeyking

Vous pouvez également utiliser boost pour cette tâche:

#include <boost/math/special_functions/fpclassify.hpp> // isnan

if( boost::math::isnan( ... ) .... )
25
math

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.

20
Johann Hibschman

Selon this , infinity est facile à vérifier:

  • signe = 0 ou 1 bit indiquant l'infini positif/négatif.
  • exposant = tous les 1 bits.
  • mantisse = tous les 0 bits.

NaN est un peu plus compliqué car il n'a pas de représentation unique:

  • signe = 0 ou 1.
  • exposant = tous les 1 bits.
  • mantisse = tout sauf les 0 bits (car tous les 0 représentent l'infini). 

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
15
Amro

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

isnan fait maintenant partie de C++ 11, inclus dans GCC++ je crois, et Apple LLVM.

Maintenant MSVC++ a une fonction _isnan dans <float.h>.

#defines et #includes 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 .

7
bobobobo

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.

6
paxdiablo

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))
2
Dexter

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.

1
rmacheshire

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

L'article suivant présente quelques astuces intéressantes pour isnan et isinf: http://jacksondunstan.com/articles/983

0
Rupert