Question

Existe-t-il une fonction isnan() ?

PS. :j'en suis MinGW (si ça fait une différence).

J'ai résolu ce problème en utilisant isnan() de <math.h>, qui n'existe pas dans <cmath>, ce que j'étais #includeing au début.

Était-ce utile?

La solution

Selon la norme IEEE, les valeurs NaN ont la propriété étrange que les comparaisons qui les concernent sont toujours false. Cela est, pour un f flotteur, f != f sera vrai que si f est NaN.

Notez que, comme certains commentaires ci-dessous ont souligné, tous les compilateurs respectent pas lorsque le code optimisation.

Pour tout compilateur qui prétend utiliser à virgule flottante IEEE, cette astuce devrait travail. Mais je ne peux pas garantir que dans la pratique. Vérifiez auprès de votre compilateur, en cas de doute.

Autres conseils

Il n'y a pas isnan() fonction disponible dans la bibliothèque standard C++ actuelle.Il a été introduit en C99 et défini comme un macro pas une fonction.Les éléments de la bibliothèque standard définie par C99 ne font pas partie de la norme C++ actuelle ISO/IEC 14882:1998 ni de sa mise à jour ISO/IEC 14882:2003.

En 2005, le rapport technique 1 a été proposé.Le TR1 apporte la compatibilité avec C99 au C++.Bien qu'il n'ait jamais été officiellement adopté pour devenir un standard C++, de nombreux (CCG 4.0+ ou Visual C++ 9.0+ Les implémentations C++ fournissent des fonctionnalités TR1, toutes ou seulement certaines (Visual C++ 9.0 ne fournit pas de fonctions mathématiques C99).

Si TR1 est disponible, alors cmath comprend des éléments C99 comme isnan(), isfinite(), etc.mais ils sont définis comme des fonctions et non comme des macros, généralement dans std::tr1:: espace de noms, bien que de nombreuses implémentations (c.-à-d.GCC 4+ sous Linux ou dans XCode sous Mac OS X 10.5+) les injecter directement dans std::, donc std::isnan est bien défini.

De plus, certaines implémentations de C++ font encore C99 isnan() macro disponible pour C++ (incluse via cmath ou math.h), ce qui peut provoquer davantage de confusion et les développeurs peuvent supposer qu'il s'agit d'un comportement standard.

Une note sur Viusal C++, comme mentionné ci-dessus, il ne fournit pas std::isnan ni l'un ni l'autre std::tr1::isnan, mais il fournit une fonction d'extension définie comme _isnan() qui est disponible depuis Visual C++ 6.0

Sur XCode, il y a encore plus de plaisir.Comme mentionné, GCC 4+ définit std::isnan.Pour les anciennes versions du compilateur et de la bibliothèque XCode, il semble (voici discussion pertinente), je n'ai pas eu l'occasion de vérifier moi-même) deux fonctions sont définies, __inline_isnand() sur Intel et __isnand() sur PowerPC.

Première solution: si vous utilisez C ++ 11

Comme il a été demandé, il y avait un peu de nouveaux développements: il est important de savoir que std::isnan() fait partie de C ++ 11

Synopsis

défini par l'en-tête <cmath>

bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)

Détermine si le nombre à virgule flottante est donné arg pas-a-number (NaN).

Paramètres

arg: valeur à virgule flottante

Valeur de retour

true si arg est NaN, false autrement

Référence

http://en.cppreference.com/w/cpp/numeric/ math / isnan

S'il vous plaît noter que ceci est incompatible avec -fast-math si vous utilisez g ++, voir ci-dessous pour d'autres suggestions.


Autres solutions: si vous 11 en utilisant des outils non conformes C +

Pour C99, en C, cela est mis en œuvre comme une macro isnan(c)that renvoie une valeur int. Le type de x est float, double ou long double.

Différents fournisseurs peuvent ou peuvent ne pas inclure ou non un isnan() de fonction.

La façon supposément portable pour vérifier NaN est d'utiliser la propriété IEEE 754 qui NaN est pas égal à lui-même. Dire x == x sera faux pour être x NaN

Cependant, la dernière option peut ne pas fonctionner avec tous les compilateur et certains paramètres (en particulier les paramètres d'optimisation), donc en dernier recours, vous pouvez toujours vérifier le modèle binaire ...

Il y a aussi un numérique Conversions .

Il existe trois façons "officielles": posix isnan macro , c ++ 0x isnan modèle de fonction , ou c visuelle ++ Fonction _isnan .

Malheureusement, il est assez peu pratique pour détecter que de ceux à utiliser.

Et malheureusement, il n'y a aucun moyen fiable de détecter si vous avez la représentation IEEE 754 avec NaN. La bibliothèque standard offre un moyen comme officiel (numeric_limits<double>::is_iec559). Mais dans les compilateurs de pratique tels que g ++ vis que vers le haut.

En théorie, on pourrait utiliser simplement x != x , mais les compilateurs tels que g ++ et Visual C ++ vis qui vers le haut.

Donc à la fin, test pour le spécifique NaN bitpatterns , en supposant (et nous espérons faire respecter, à un moment donné!) Une représentation particulière telle que IEEE 754.


EDIT : comme un exemple de "compilateurs tels que g ++ ... vis que jusqu'à", considérer

#include <limits>
#include <assert.h>

void foo( double a, double b )
{
    assert( a != b );
}

int main()
{
    typedef std::numeric_limits<double> Info;
    double const nan1 = Info::quiet_NaN();
    double const nan2 = Info::quiet_NaN();
    foo( nan1, nan2 );
}

Compiler avec g ++ (TDM-2 mingw32) 4.4.1:

C:\test> type "C:\Program Files\@commands\gnuc.bat"
@rem -finput-charset=windows-1252
@g++ -O -pedantic -std=c++98 -Wall -Wwrite-strings %* -Wno-long-long

C:\test> gnuc x.cpp

C:\test> a && echo works... || echo !failed
works...

C:\test> gnuc x.cpp --fast-math

C:\test> a && echo works... || echo !failed
Assertion failed: a != b, file x.cpp, line 6

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
!failed

C:\test> _

Il y a un std :: isnan si vous compilateur prend en charge les extensions de C99, mais je ne sais pas si MinGW fait.

Voici une petite fonction qui devrait fonctionner si votre compilateur ne pas la fonction standard:

bool custom_isnan(double var)
{
    volatile double d = var;
    return d != d;
}

Vous pouvez utiliser numeric_limits<float>::quiet_NaN( ) défini dans la bibliothèque standard limits pour tester avec. Il y a une constante séparée définie pour double.

#include <iostream>
#include <math.h>
#include <limits>

using namespace std;

int main( )
{
   cout << "The quiet NaN for type float is:  "
        << numeric_limits<float>::quiet_NaN( )
        << endl;

   float f_nan = numeric_limits<float>::quiet_NaN();

   if( isnan(f_nan) )
   {
       cout << "Float was Not a Number: " << f_nan << endl;
   }

   return 0;
}

Je ne sais pas si cela fonctionne sur toutes les plateformes, comme je l'ai seulement été testé avec g ++ sur Linux.

Vous pouvez utiliser la fonction isnan(), mais vous devez inclure la bibliothèque mathématique C.

#include <cmath>

Cette fonction fait partie de C99, ce n'est pas disponible partout. Si votre fournisseur ne fournit pas la fonction, vous pouvez également définir votre propre variante pour la compatibilité.

inline bool isnan(double x) {
    return x != x;
}

Prévention nan

Ma réponse à cette question est ne pas utiliser des contrôles rétroactifs pour nan . Utilisez préventives contrôles pour les divisions de la forme 0.0/0.0 à la place.

#include <float.h>
float x=0.f ;             // I'm gonna divide by x!
if( !x )                  // Wait! Let me check if x is 0
  x = FLT_MIN ;           // oh, since x was 0, i'll just make it really small instead.
float y = 0.f / x ;       // whew, `nan` didn't appear.

nan résultats de l'opération 0.f/0.f ou 0.0/0.0. nan est un terrible ennemi juré à la stabilité de votre code qui doit être détecté et empêché très soigneusement 1 . Les propriétés de nan qui sont différents des nombres normaux:

  • nan est toxique, (5 * nan = nan)
  • nan ne correspond pas à quoi que ce soit, même pas lui-même (nan! = nan)
  • nan pas plus grand que tout (nan!> 0)
  • nan est pas moins que tout (nan! <0)

Les 2 dernières propriétés énumérées sont contre-logique et se traduira par un comportement étrange de code qui repose sur des comparaisons avec un certain nombre de nan (3ème dernière propriété est trop bizarre, mais vous êtes probablement jamais aller voir x != x ? dans votre code (à moins que vous vérifiez pour nan (unreliably))).

Dans mon propre code, je remarquai que les valeurs de nan ont tendance à produire des bugs difficiles à trouver. (Notez comment cela est pas le cas pour inf ou -inf. (-inf <0) retourne retourne TRUE, (0 inf) TRUE, et même (-inf <inf) retourne TRUE. Donc, dans mon l'expérience, le comportement du code est souvent toujours comme on le souhaite).

ce qu'il faut faire en nan

Qu'est-ce que vous voulez arriver sous 0.0/0.0 doit être traitée comme un cas particulier , mais ce que vous faites doit dépendre des chiffres que vous attendez de sortir du code.

Dans l'exemple ci-dessus, le résultat de (0.f/FLT_MIN) sera 0, essentiellement. Vous voudrez peut-0.0/0.0 générer HUGE à la place. Ainsi,

float x=0.f, y=0.f, z;
if( !x && !y )    // 0.f/0.f case
  z = FLT_MAX ;   // biggest float possible
else
  z = y/x ;       // regular division.

Donc, en ce qui précède, si x étaient 0.f, inf entraînerait (qui a très bon / comportement non destructif comme mentionné ci-dessus en fait).

Rappelez-vous, division par 0 provoque une exception d'exécution . Donc, vous devez toujours vérifier la division entière par 0. Tout simplement parce que 0.0/0.0 évalue tranquillement nan ne signifie pas que vous pouvez être paresseux et ne pas vérifier 0.0/0.0 avant qu'elle ne survienne.

1 Vérifie nan via x != x sont parfois peu fiables (x != x dépouillée par certains compilateurs optimisation qui rompent la conformité IEEE, en particulier lorsque le commutateur -ffast-math est activé).

Le code suivant utilise la définition de NAN (tous les bits d'exposant fixés, au moins un bit fractionnaire) et suppose que = sizeof (float) de sizeof (int) = 4. Vous pouvez consulter NAN dans Wikipedia pour les détails.

bool IsNan( float value ) { return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000; }

14 C ++ il y a un certain nombre de façons de tester si un nombre à virgule flottante value est un NAN.

De ces façons, que vérification des bits de la représentation du nombre, fonctionne de manière fiable, comme indiqué dans ma réponse initiale. En particulier, std::isnan et la v != v de contrôle souvent proposé, ne fonctionnent pas de manière fiable et ne devrait pas être utilisé, de peur que votre code ne fonctionne plus correctement quand quelqu'un décide que l'optimisation de virgule flottante est nécessaire, et demande au compilateur de le faire. Cette situation peut changer, les compilateurs peuvent obtenir plus conforme, mais pour cette question qui n'a pas eu lieu au cours des 6 années écoulées depuis la réponse initiale.

Pour environ 6 ans ma réponse initiale a été la solution retenue pour cette question, qui était OK. Mais récemment, une réponse très upvoted recommander le test v != v peu fiable a été sélectionné. D'où cette supplémentaire plus à jour réponse (nous avons maintenant le C ++ 11 et 14 C ++ normes, et 17 C ++ à l'horizon).


Les principaux moyens à vérifier pour NaN-ness, en date du 14 C ++, sont les suivants:

  • std::isnan(value) )
    est la voie de la bibliothèque standard C ++ prévue depuis 11. isnan conflits apparemment avec le macro Posix du même nom, mais dans la pratique qui n'est pas un problème. Le principal problème est que lorsque l'optimisation arithmétique en virgule flottante est requis, avec au moins un compilateur principal, à savoir g ++, std::isnan renvoie false pour l'argument NaN .

  • (fpclassify(value) == FP_NAN) )
    Souffre du même problème que std::isnan, à savoir, n'est pas fiable.

  • (value != value) )
    Recommandé dans de nombreuses réponses SO. Souffre du même problème que std::isnan, à savoir, n'est pas fiable.

  • (value == Fp_info::quiet_NaN()) )
    Ceci est un test avec le comportement standard ne devrait pas détecter NaN, mais avec le un comportement optimisé pourrait peut-être détecter NaN (en raison du code optimisé simplement comparer les représentations bitlevel directement), et peut-être associés à une autre façon de couvrir la norme un comportement optimisé, peut détecter de manière fiable NaN. Malheureusement il est avéré ne pas fonctionner de manière fiable.

  • (ilogb(value) == FP_ILOGBNAN) )
    Souffre du même problème que std::isnan, à savoir, n'est pas fiable.

  • isunordered(1.2345, value) )
    Souffre du même problème que std::isnan, à savoir, n'est pas fiable.

  • is_ieee754_nan( value ) )
    Ce n'est pas une fonction standard. Il est la vérification des bits selon la norme IEEE 754 la norme. Il est tout à fait fiable mais le code est un peu dépendant du système.


Dans le code de test complet après le « succès » est de savoir si une expression rapporte Nan-ness de la valeur. Pour la plupart des expressions de cette mesure du succès, l'objectif de détection NaN et que NaN, correspond à leur sémantique standard. Pour l'expression de (value == Fp_info::quiet_NaN()) ), cependant, le comportement standard est qu'il ne fonctionne pas comme un détecteur NaN.

#include <cmath>        // std::isnan, std::fpclassify
#include <iostream>
#include <iomanip>      // std::setw
#include <limits>
#include <limits.h>     // CHAR_BIT
#include <sstream>
#include <stdint.h>     // uint64_t
using namespace std;

#define TEST( x, expr, expected ) \
    [&](){ \
        const auto value = x; \
        const bool result = expr; \
        ostringstream stream; \
        stream << boolalpha << #x " = " << x << ", (" #expr ") = " << result; \
        cout \
            << setw( 60 ) << stream.str() << "  " \
            << (result == expected? "Success" : "FAILED") \
            << endl; \
    }()

#define TEST_ALL_VARIABLES( expression ) \
    TEST( v, expression, true ); \
    TEST( u, expression, false ); \
    TEST( w, expression, false )

using Fp_info = numeric_limits<double>;

inline auto is_ieee754_nan( double const x )
    -> bool
{
    static constexpr bool   is_claimed_ieee754  = Fp_info::is_iec559;
    static constexpr int    n_bits_per_byte     = CHAR_BIT;
    using Byte = unsigned char;

    static_assert( is_claimed_ieee754, "!" );
    static_assert( n_bits_per_byte == 8, "!" );
    static_assert( sizeof( x ) == sizeof( uint64_t ), "!" );

    #ifdef _MSC_VER
        uint64_t const bits = reinterpret_cast<uint64_t const&>( x );
    #else
        Byte bytes[sizeof(x)];
        memcpy( bytes, &x, sizeof( x ) );
        uint64_t int_value;
        memcpy( &int_value, bytes, sizeof( x ) );
        uint64_t const& bits = int_value;
    #endif

    static constexpr uint64_t   sign_mask       = 0x8000000000000000;
    static constexpr uint64_t   exp_mask        = 0x7FF0000000000000;
    static constexpr uint64_t   mantissa_mask   = 0x000FFFFFFFFFFFFF;

    (void) sign_mask;
    return (bits & exp_mask) == exp_mask and (bits & mantissa_mask) != 0;
}

auto main()
    -> int
{
    double const v = Fp_info::quiet_NaN();
    double const u = 3.14;
    double const w = Fp_info::infinity();

    cout << boolalpha << left;
    cout << "Compiler claims IEEE 754 = " << Fp_info::is_iec559 << endl;
    cout << endl;;
    TEST_ALL_VARIABLES( std::isnan(value) );                    cout << endl;
    TEST_ALL_VARIABLES( (fpclassify(value) == FP_NAN) );        cout << endl;
    TEST_ALL_VARIABLES( (value != value) );                     cout << endl;
    TEST_ALL_VARIABLES( (value == Fp_info::quiet_NaN()) );      cout << endl;
    TEST_ALL_VARIABLES( (ilogb(value) == FP_ILOGBNAN) );        cout << endl;
    TEST_ALL_VARIABLES( isunordered(1.2345, value) );           cout << endl;
    TEST_ALL_VARIABLES( is_ieee754_nan( value ) );
}

Résultats avec g ++ (note encore une fois que le comportement standard de (value == Fp_info::quiet_NaN()) est qu'il ne fonctionne pas comme un NaN-détecteur, il est juste très pratique d'intérêt ici):

[C:\my\forums\so\282  (detect NaN)]
> g++ --version | find "++"
g++ (x86_64-win32-sjlj-rev1, Built by MinGW-W64 project) 6.3.0

[C:\my\forums\so\282  (detect NaN)]
> g++ foo.cpp && a
Compiler claims IEEE 754 = true

v = nan, (std::isnan(value)) = true                           Success
u = 3.14, (std::isnan(value)) = false                         Success
w = inf, (std::isnan(value)) = false                          Success

v = nan, ((fpclassify(value) == 0x0100)) = true               Success
u = 3.14, ((fpclassify(value) == 0x0100)) = false             Success
w = inf, ((fpclassify(value) == 0x0100)) = false              Success

v = nan, ((value != value)) = true                            Success
u = 3.14, ((value != value)) = false                          Success
w = inf, ((value != value)) = false                           Success

v = nan, ((value == Fp_info::quiet_NaN())) = false            FAILED
u = 3.14, ((value == Fp_info::quiet_NaN())) = false           Success
w = inf, ((value == Fp_info::quiet_NaN())) = false            Success

v = nan, ((ilogb(value) == ((int)0x80000000))) = true         Success
u = 3.14, ((ilogb(value) == ((int)0x80000000))) = false       Success
w = inf, ((ilogb(value) == ((int)0x80000000))) = false        Success

v = nan, (isunordered(1.2345, value)) = true                  Success
u = 3.14, (isunordered(1.2345, value)) = false                Success
w = inf, (isunordered(1.2345, value)) = false                 Success

v = nan, (is_ieee754_nan( value )) = true                     Success
u = 3.14, (is_ieee754_nan( value )) = false                   Success
w = inf, (is_ieee754_nan( value )) = false                    Success

[C:\my\forums\so\282  (detect NaN)]
> g++ foo.cpp -ffast-math && a
Compiler claims IEEE 754 = true

v = nan, (std::isnan(value)) = false                          FAILED
u = 3.14, (std::isnan(value)) = false                         Success
w = inf, (std::isnan(value)) = false                          Success

v = nan, ((fpclassify(value) == 0x0100)) = false              FAILED
u = 3.14, ((fpclassify(value) == 0x0100)) = false             Success
w = inf, ((fpclassify(value) == 0x0100)) = false              Success

v = nan, ((value != value)) = false                           FAILED
u = 3.14, ((value != value)) = false                          Success
w = inf, ((value != value)) = false                           Success

v = nan, ((value == Fp_info::quiet_NaN())) = true             Success
u = 3.14, ((value == Fp_info::quiet_NaN())) = true            FAILED
w = inf, ((value == Fp_info::quiet_NaN())) = true             FAILED

v = nan, ((ilogb(value) == ((int)0x80000000))) = true         Success
u = 3.14, ((ilogb(value) == ((int)0x80000000))) = false       Success
w = inf, ((ilogb(value) == ((int)0x80000000))) = false        Success

v = nan, (isunordered(1.2345, value)) = false                 FAILED
u = 3.14, (isunordered(1.2345, value)) = false                Success
w = inf, (isunordered(1.2345, value)) = false                 Success

v = nan, (is_ieee754_nan( value )) = true                     Success
u = 3.14, (is_ieee754_nan( value )) = false                   Success
w = inf, (is_ieee754_nan( value )) = false                    Success

[C:\my\forums\so\282  (detect NaN)]
> _

Résultats avec Visual C ++:

[C:\my\forums\so\282  (detect NaN)]
> cl /nologo- 2>&1 | find "++"
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23725 for x86

[C:\my\forums\so\282  (detect NaN)]
> cl foo.cpp /Feb && b
foo.cpp
Compiler claims IEEE 754 = true

v = nan, (std::isnan(value)) = true                           Success
u = 3.14, (std::isnan(value)) = false                         Success
w = inf, (std::isnan(value)) = false                          Success

v = nan, ((fpclassify(value) == 2)) = true                    Success
u = 3.14, ((fpclassify(value) == 2)) = false                  Success
w = inf, ((fpclassify(value) == 2)) = false                   Success

v = nan, ((value != value)) = true                            Success
u = 3.14, ((value != value)) = false                          Success
w = inf, ((value != value)) = false                           Success

v = nan, ((value == Fp_info::quiet_NaN())) = false            FAILED
u = 3.14, ((value == Fp_info::quiet_NaN())) = false           Success
w = inf, ((value == Fp_info::quiet_NaN())) = false            Success

v = nan, ((ilogb(value) == 0x7fffffff)) = true                Success
u = 3.14, ((ilogb(value) == 0x7fffffff)) = false              Success
w = inf, ((ilogb(value) == 0x7fffffff)) = true                FAILED

v = nan, (isunordered(1.2345, value)) = true                  Success
u = 3.14, (isunordered(1.2345, value)) = false                Success
w = inf, (isunordered(1.2345, value)) = false                 Success

v = nan, (is_ieee754_nan( value )) = true                     Success
u = 3.14, (is_ieee754_nan( value )) = false                   Success
w = inf, (is_ieee754_nan( value )) = false                    Success

[C:\my\forums\so\282  (detect NaN)]
> cl foo.cpp /Feb /fp:fast && b
foo.cpp
Compiler claims IEEE 754 = true

v = nan, (std::isnan(value)) = true                           Success
u = 3.14, (std::isnan(value)) = false                         Success
w = inf, (std::isnan(value)) = false                          Success

v = nan, ((fpclassify(value) == 2)) = true                    Success
u = 3.14, ((fpclassify(value) == 2)) = false                  Success
w = inf, ((fpclassify(value) == 2)) = false                   Success

v = nan, ((value != value)) = true                            Success
u = 3.14, ((value != value)) = false                          Success
w = inf, ((value != value)) = false                           Success

v = nan, ((value == Fp_info::quiet_NaN())) = false            FAILED
u = 3.14, ((value == Fp_info::quiet_NaN())) = false           Success
w = inf, ((value == Fp_info::quiet_NaN())) = false            Success

v = nan, ((ilogb(value) == 0x7fffffff)) = true                Success
u = 3.14, ((ilogb(value) == 0x7fffffff)) = false              Success
w = inf, ((ilogb(value) == 0x7fffffff)) = true                FAILED

v = nan, (isunordered(1.2345, value)) = true                  Success
u = 3.14, (isunordered(1.2345, value)) = false                Success
w = inf, (isunordered(1.2345, value)) = false                 Success

v = nan, (is_ieee754_nan( value )) = true                     Success
u = 3.14, (is_ieee754_nan( value )) = false                   Success
w = inf, (is_ieee754_nan( value )) = false                    Success

[C:\my\forums\so\282  (detect NaN)]
> _

Résumant les résultats ci-dessus, seuls des tests directs de la représentation au niveau du bit, en utilisant la fonction is_ieee754_nan définie dans ce programme d'essai, a travaillé de manière fiable dans tous les cas avec les deux g ++ et Visual C ++.


Addendum:
Après avoir affiché ce qui précède, je pris conscience d'une autre possible de tester NaN, mentionné dans une autre réponse ici, à savoir ((value < 0) == (value >= 0)). Cela se est avéré fonctionner correctement avec Visual C ++, mais a échoué avec l'option g de -ffast-math de '++. Seuls des tests directe fonctionne de manière fiable profil binaire.

inline bool IsNan(float f)
{
    const uint32 u = *(uint32*)&f;
    return (u&0x7F800000) == 0x7F800000 && (u&0x7FFFFF);    // Both NaN and qNan.
}

inline bool IsNan(double d)
{
    const uint64 u = *(uint64*)&d;
    return (u&0x7FF0000000000000ULL) == 0x7FF0000000000000ULL && (u&0xFFFFFFFFFFFFFULL);
}

Cela fonctionne si sizeof(int) est 4 et sizeof(long long) est 8.

Au cours de l'exécution, il est seule comparaison, pièces moulées ne prennent pas de temps. Il change simplement la configuration des indicateurs de comparaison pour vérifier l'égalité.

Une solution possible qui ne serait pas dépendre de la représentation spécifique de l'IEEE NaN utilisé serait la suivante:

template<class T>
bool isnan( T f ) {
    T _nan =  (T)0.0/(T)0.0;
    return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}

Quant à moi, la solution pourrait être une macro pour le faire explicitement en ligne et donc assez vite. Il fonctionne également pour tout type de flotteur. Il fonde sur le fait que le seul cas lorsqu'une valeur est égale à elle-même n'est quand la valeur est un nombre.

#ifndef isnan
  #define isnan(a) (a != a)
#endif

Considérant que (! X = x) n'est pas toujours garanti NaN (par exemple si vous utilisez l'option -ffast-math), je l'ai utilisé:

#define IS_NAN(x) (((x) < 0) == ((x) >= 0))

Les chiffres ne peuvent pas être à la fois <0 et> = 0, donc vraiment ce contrôle passe que si le nombre est ni inférieur, ni supérieur ou égal à zéro. Ce qui est fondamentalement aucun numéro du tout, ou NaN.

Vous pouvez également utiliser si vous préférez:

#define IS_NAN(x) (!((x)<0) && !((x)>=0)

Je ne sais pas comment cela est affecté par -ffast-mathématiques, donc votre kilométrage peut varier.

Cela fonctionne:

#include <iostream>
#include <math.h>
using namespace std;

int main ()
{
  char ch='a';
  double val = nan(&ch);
  if(isnan(val))
     cout << "isnan" << endl;

  return 0;
}

sortie: isnan

Il me semble que la meilleure approche véritablement multi-plateforme serait d'utiliser un syndicat et de tester la configuration binaire du double pour vérifier NaN.

Je ne l'ai pas testé à fond cette solution, et il peut y avoir un moyen plus efficace de travailler avec les motifs de bits, mais je pense que cela devrait fonctionner.

#include <stdint.h>
#include <stdio.h>

union NaN
{
    uint64_t bits;
    double num;
};

int main()
{
    //Test if a double is NaN
    double d = 0.0 / 0.0;
    union NaN n;
    n.num = d;
    if((n.bits | 0x800FFFFFFFFFFFFF) == 0xFFFFFFFFFFFFFFFF)
    {
        printf("NaN: %f", d);
    }

    return 0;
}

La norme IEEE dit lorsque l'exposant est tous 1s et la mantisse est non nul, le nombre est un NaN. Double est un peu signe 1, bits d'exposant de 11 et 52 mantisse morceaux. Faites une vérification de bits.

Sur x86-64 vous pouvez avoir des méthodes extrêmement rapides pour le contrôle NaN et l'infini, qui fonctionnent indépendamment de l'option du compilateur -ffast-math. (f != f, std::isnan, std::isinf toujours donné false avec -ffast-math).


Test de NaN, l'infini et un nombre fini peut facilement être fait en vérifiant exposant maximum. l'infini est exposant maximum à zéro mantisse, NaN est maximum exposant et la mantisse non nulle. L'exposant est stocké dans les bits suivants après le bit de signe le plus élevé, de sorte que nous pouvons juste décalage vers la gauche pour se débarrasser du bit de signe et de faire l'exposant les bits de premier niveau, pas de masquage (operator&) est nécessaire:

static inline uint64_t load_ieee754_rep(double a) {
    uint64_t r;
    static_assert(sizeof r == sizeof a, "Unexpected sizes.");
    std::memcpy(&r, &a, sizeof a); // Generates movq instruction.
    return r;
}

static inline uint32_t load_ieee754_rep(float a) {
    uint32_t r;
    static_assert(sizeof r == sizeof a, "Unexpected sizes.");
    std::memcpy(&r, &a, sizeof a); // Generates movd instruction.
    return r;
}

constexpr uint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);
constexpr uint32_t inf_float_shl1 = UINT32_C(0xff000000);

// The shift left removes the sign bit. The exponent moves into the topmost bits,
// so that plain unsigned comparison is enough.
static inline bool isnan2(double a)    { return load_ieee754_rep(a) << 1  > inf_double_shl1; }
static inline bool isinf2(double a)    { return load_ieee754_rep(a) << 1 == inf_double_shl1; }
static inline bool isfinite2(double a) { return load_ieee754_rep(a) << 1  < inf_double_shl1; }
static inline bool isnan2(float a)     { return load_ieee754_rep(a) << 1  > inf_float_shl1; }
static inline bool isinf2(float a)     { return load_ieee754_rep(a) << 1 == inf_float_shl1; }
static inline bool isfinite2(float a)  { return load_ieee754_rep(a) << 1  < inf_float_shl1; }

Les versions std de charge isinf et isfinite 2 constantes de double/float de segment de .data et dans le pire des cas, ils peuvent causer des 2 défauts de cache de données. Les versions ci-dessus ne se chargent pas toutes les données, les constantes de inf_double_shl1 et inf_float_shl1 obtenir codées comme opérandes immédiate dans les instructions de montage.


isnan2 plus rapide est à seulement 2 instructions de montage:

bool isnan2(double a) {
    bool r;
    asm(".intel_syntax noprefix"
        "\n\t ucomisd %1, %1"
        "\n\t setp %b0"
        "\n\t .att_syntax prefix"
        : "=g" (r)
        : "x" (a)
        : "cc"
        );
    return r;
}

Utilise le fait que ucomisd instruction fixe indicateur de parité si un argument est NaN. Voici comment fonctionne std::isnan quand aucune option de -ffast-math est spécifié.

Cette fonction détecte l'infini et aussi NaN dans Visual Studio en cochant la case est dans les limites doubles:

//#include <float.h>
double x, y = -1.1; x = sqrt(y);
if (x >= DBL_MIN && x <= DBL_MAX )
    cout << "DETECTOR-2 of errors FAILS" << endl;
else
    cout << "DETECTOR-2 of errors OK" << endl;

Comme les commentaires ci-dessus un état! = A ne fonctionnera pas en g ++ et d'autres compilateurs, mais cette astuce devraient. Il ne peut pas être aussi efficace, mais il est encore une façon:

bool IsNan(float a)
{
    char s[4];
    sprintf(s, "%.3f", a);
    if (s[0]=='n') return true;
    else return false;
}

En fait, en g ++ (je ne suis pas sûr d'autres cependant) imprime printf « nan » sur% d ou% de formats .F si la variable n'est pas un entier valide / flottant. Par conséquent, ce code est la vérification pour le premier caractère de la chaîne à être « n » (comme dans « nan »)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top