Vérifier si un double (ou un float) est NaN en C++

y a-t-il une fonction isnan ()?

PS.: Je suis dans le MinGW (si cela fait une différence).

j'ai résolu cela en utilisant isnan() de <math.h> , qui n'existe pas dans <cmath> , que j'étais #include ing au début.

328
demandé sur Peter Mortensen 2009-02-20 21:09:53

20 réponses

selon la norme de L'IEEE, les valeurs de NaN ont la propriété étrange que les comparaisons les concernant sont toujours false. C'est-à-dire, pour un flotteur f, f != f sera vrai seulement si f est NaN.

notez que, comme certains commentaires ci-dessous l'ont souligné, ce ne sont pas tous les compilateurs qui le respectent lors de l'optimisation du code.

pour tout compilateur qui prétend utiliser IEEE floating point, cette astuce devrait marcher. Mais je ne peux pas garantir qu'il va travailler dans la pratique. Vérifiez auprès de votre compilateur, en cas de doute.

317
répondu jalf 2012-06-26 16:28:20

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

En 2005 Rapport Technique 1 a été proposé. Le TR1 apporte la compatibilité avec C99 à C++. En dépit du fait qu'il a jamais été officiellement adopté pour devenir la norme C++, beaucoup ( GCC 4.0+ ou Visual C++ 9.0+ c++ implémentations ne fournissent des fonctionnalités TR1, tous ou seulement certains (Visual C++ 9.0 ne fournit pas C99 fonctions mathématiques).

si TR1 est disponible, alors cmath inclut les éléments C99 comme isnan() , isfinite() , etc. mais ils sont définis comme des fonctions, pas des macros, habituellement dans std::tr1:: espace de nom, bien que beaucoup les implémentations (c.-à-d. GCC 4+ sur Linux ou XCode sur Mac OS X 10.5+) les injectent directement dans std:: , donc std::isnan est bien défini.

de plus, certaines implémentations de C++ rendent encore C99 isnan() macro disponible pour C++ (inclus dans cmath ou math.h ), ce qui peut causer plus de confusions et les développeurs peuvent supposer que c'est un comportement standard.

Une remarque à propos de Viusal C++, comme mentionné ci-dessus, il ne fournit pas std::isnan ni std::tr1::isnan , mais il fournit une fonction d'extension définie comme _isnan() qui a été disponible depuis visuel c++ 6.0

sur XCode, il y a encore plus de plaisir. Comme mentionné, GCC 4 + définit std::isnan . Pour les versions plus anciennes du compilateur et de la bibliothèque forme XCode, il semble (voici discussion pertinente ), n'ont pas eu la chance de me vérifier) deux fonctions sont définies, __inline_isnand() sur Intel et __isnand() sur Power PC.

206
répondu mloskot 2010-01-23 16:19:05

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

puisque cela 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 dans 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 de virgule flottante arg donné n'est pas un nombre-a ( 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

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


autres solutions: si vous utilisez des outils non conformes à C++11

Pour C99, en C, il est implémenté sous forme d'une macro isnan(c) qui renvoie un int valeur. Le type de x doit être flottant, double ou double.

divers vendeurs peuvent inclure ou non une fonction isnan() .

la façon supposée portable de vérifier pour NaN est d'utiliser la propriété IEEE 754 que NaN n'est pas égal à lui-même: i.e. x == x sera faux pour x être NaN .

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

133
répondu BlueTrin 2017-02-10 18:13:18

il y a aussi une header-only library présente dans Boost qui ont des outils soignés pour traiter les types de données à virgule flottante

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

vous obtenez les fonctions suivantes:

template <class T> bool isfinite(T z);
template <class T> bool isinf(T t);
template <class T> bool isnan(T t);
template <class T> bool isnormal(T t);

si vous avez le temps, puis jeter un oeil à la boîte à outils complète en mathématiques de Boost, il a de nombreux outils utiles et se développe rapidement.

aussi quand il s'agit de points flottants et non-flottants il pourrait être une bonne idée de regardez le conversions numériques .

81
répondu Anonymous 2010-02-22 18:52:22

il y a trois façons" officielles": posix isnan macro , c++0x isnan modèle de fonction , ou visual c++ _isnan fonction .

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

et malheureusement, il n'y a aucun moyen fiable pour détecter si vous avez une représentation IEEE 754 avec NaNs. La bibliothèque standard offre officielle de telle façon ( numeric_limits<double>::is_iec559 ). Mais dans la pratique, des compilateurs tels que g++ font tout foirer.

en théorie on pourrait utiliser simplement x != x , mais des compilateurs tels que G++ et visual C++ font tout foirer.

ainsi, à la fin, test pour le spécifique Nan bitpatterns , en supposant (et en espérant appliquer, à un moment donné!) une représentation particulière telle que IEEE 754.


MODIFIER : un exemple de "compilateurs tels que g++ ... vis", pensez

#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 );
}

compilant 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> _
42
répondu Cheers and hth. - Alf 2017-02-07 21:00:58

il y a une std::isnan si vous compilez des extensions C99, mais je ne suis pas sûr que mingw le fasse.

Voici une petite fonction qui devrait fonctionner si votre compilateur n'a pas la fonction standard:

bool custom_isnan(double var)
{
    volatile double d = var;
    return d != d;
}
38
répondu CTT 2009-02-20 18:14:58

vous pouvez utiliser numeric_limits<float>::quiet_NaN( ) défini dans la limits bibliothèque standard 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, car je n'ai testé g++ que sur Linux.

25
répondu Bill the Lizard 2009-02-20 19:18:12

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

#include <cmath>

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

inline bool isnan(double x) {
    return x != x;
}
17
répondu raimue 2018-06-07 09:34:32

nan "prévention de la 1519440920"

ma réponse à cette question Est n'utilisez pas de chèques rétroactifs pour nan . Utilisez plutôt préventif contrôle des divisions du formulaire 0.0/0.0 .

#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ésulte de l'opération 0.f/0.f , ou 0.0/0.0 . nan est un terrible ennemi pour la stabilité de votre code qui doit être détecté et prévenu très soigneusement 1 . Les propriétés de nan qui sont différentes des nombres normaux:

  • nan est toxique, (5* nan = nan )
  • nan n'est rien, pas même lui-même ( nan != nan )
  • nan pas plus que n'importe quoi ( nan !> 0)
  • nan n'est pas moins que n'importe quoi ( nan !< 0)

les 2 dernières propriétés listées sont contre-logiques et résulteront en un comportement Impair du code qui s'appuie sur des comparaisons avec un nombre nan (la 3ème dernière propriété est impair aussi mais vous n'allez probablement jamais voir x != x ? dans votre code (à moins que vous ne vérifiiez pour nan (de manière peu fiable))).

dans mon propre code, j'ai remarqué que les valeurs de nan tendent produire des bugs difficiles à trouver. (Notez comment ceci est et non le cas pour inf ou -inf . ( -inf < 0) retours TRUE , ( 0 < inf ) renvoie TRUE, et même ( -inf < inf ) renvoie TRUE. Donc, d'après mon expérience, le comportement du code est souvent toujours comme désiré).

que faire en vertu de l'nan

Ce que vous voulez arriver sous 0.0/0.0 doit être traité comme un cas spécial , mais ce que vous ne doivent dépendre que les numéros que vous attendez du code.

Dans l'exemple ci-dessus, le résultat de ( 0.f/FLT_MIN ) sera 0 , en gros. Vous pouvez vouloir 0.0/0.0 pour générer HUGE à la place. So,

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 dans ce qui précède, si x était 0.f , inf résulterait (qui a un comportement assez bon/non destructif comme mentionnés ci-dessus en fait).

Souvenez-vous, integer division par 0 provoque une exception d'exécution . Donc vous devez toujours vérifier la division entière par 0. Ce n'est pas parce que 0.0/0.0 est discrètement évalué à nan que vous pouvez être paresseux et ne pas vérifier 0.0/0.0 avant qu'il n'arrive.

1 les contrôles pour nan via x != x sont parfois peu fiables ( x != x étant dépouillé par certains optimiser les compilateurs qui ne respectent pas la conformité IEEE, en particulier lorsque le commutateur -ffast-math est activé).

13
répondu bobobobo 2017-05-23 12:03:05

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

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

11
répondu Ian 2011-04-05 01:53:33
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.

pendant la durée d'exécution il ne s'agit que d'une comparaison, les moulages ne prennent pas de temps. Il ne fait que modifier la configuration des indicateurs de comparaison pour vérifier l'égalité.

7
répondu ST3 2014-10-19 21:56:13

à partir de C++14 Il y a plusieurs façons de tester si un nombre de virgule flottante value est un NaN.

de ces façons, seulement vérification des bits de la représentation du nombre, fonctionne de manière fiable, comme je l'ai indiqué dans ma réponse initiale. En particulier, std::isnan et le contrôle souvent proposé v != v , ne fonctionnent pas de manière fiable et ne devraient pas être utilisés, de peur que votre code cesse de fonctionner correctement lorsque quelqu'un décide que le point flottant l'optimisation 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 dans les 6 années depuis la réponse originale.

pendant environ 6 ans ma réponse originale était la solution choisie pour cette question, qui était OK. Mais récemment, une réponse très positive recommandant le test peu fiable v != v a été choisie. D'où cette réponse plus récente (nous avons maintenant le C++11 et C++14, et C++17 à l'horizon).


les principaux moyens de vérifier la présence de NaN-ness, à partir de C++14, sont:

  • std::isnan(value) )

    est la bibliothèque standard prévue depuis C++11. isnan est apparemment en conflit avec POSIX macro du même nom, mais en pratique ce n'est pas un problème. Le principal problème est que lorsque l'optimisation arithmétique flottante point est demandé, puis avec au moins un compilateur principal, à savoir g++, std::isnan retourne false pour l'argument NaN .

  • (fpclassify(value) == FP_NAN) )

    Souffre du même problème que std::isnan , c'est à dire, n'est pas fiable.

  • (value != value) )

    Recommandé dans de nombreuses réponses. Souffre du même problème que std::isnan , c'est à dire, n'est pas fiable.

  • (value == Fp_info::quiet_NaN()) )

    Il s'agit d'un test qui avec le comportement standard ne devrait pas détecter NaNs, mais que, avec le le comportement optimisé peut-être pourrait détecter NaNs (en raison de code optimisé ne comparant les représentations bitlevel directement), et peut-être combiné avec une autre façon de couvrir le comportement standard non-optimisé, pourrait détecter de manière fiable NaN. Malheureusement il s'est avéré ne pas fonctionner de manière fiable.

  • (ilogb(value) == FP_ILOGBNAN) )

    Souffre du même problème que std::isnan , c'est à dire, n'est pas fiable.

  • isunordered(1.2345, value) )

    Souffre du même problème que std::isnan , c'est à dire, n'est pas fiable.

  • is_ieee754_nan( value ) )

    Ce n'est pas une fonction standard. C'est la vérification des bits selon le IEEE 754 norme. C'est complètement fiable mais le code est quelque peu dépendant du système.


dans le code de test complet suivant "success" est si une expression signale Nan-ness de la valeur. Pour la plupart des expressions, cette mesure du succès, le but de la détection de NaNs et seulement NaNs, correspond à leur sémantique standard. Pour l'expression (value == Fp_info::quiet_NaN()) ) , cependant, le le comportement standard est qu'il ne fonctionne pas comme un NaN-détecteur.

#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++ (notez encore que le comportement standard de (value == Fp_info::quiet_NaN()) est qu'il ne fonctionne pas comme un NaN-détecteur, il est juste beaucoup d'intérêt pratique ici):

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

[C:\my\forums\so2  (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\so2  (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\so2  (detect NaN)]
> _

résultats avec C visuel++:

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

[C:\my\forums\so2  (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\so2  (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\so2  (detect NaN)]
> _

résumant les résultats ci-dessus, seulement l'essai direct de la représentation de niveau de bits, en utilisant le is_ieee754_nan fonction définie dans ce programme de test, fonctionnait de manière fiable dans tous les cas avec G++ et Visual C++.


Addendum:

Après avoir posté ce qui précède, j'ai pris conscience d'une autre possibilité de tester pour NaN, mentionné dans une autre réponse ici, à savoir ((value < 0) == (value >= 0)) . Cela s'est avéré très bien fonctionner avec Visual C++ mais a échoué avec l'option -ffast-math de g++. Seuls les tests de bitpattern direct fonctionnent de manière fiable.

6
répondu Cheers and hth. - Alf 2017-05-23 11:55:05

une solution possible qui ne dépendrait pas de la représentation spécifique de L'IEEE pour la NaN utilisée 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) );
}
4
répondu Dan Nathan 2012-04-03 22:03:16

quant à moi la solution pourrait être une macro pour la rendre explicitement en ligne et donc assez rapide. Il fonctionne également pour tout type de flotteur. Il se base sur le fait que le seul cas où une valeur n'est pas égale est quand la valeur n'est pas un nombre.

#ifndef isnan
  #define isnan(a) (a != a)
#endif
4
répondu user1705817 2012-12-11 09:08:56

après avoir lu les autres réponses, je voulais quelque chose qui passerait à travers l'avertissement de comparaison en virgule flottante et qui ne se briserait pas sous les Maths Rapides. Le code suivant semble fonctionner:

/*
  Portable warning-free NaN test:
    * Does not emit warning with -Wfloat-equal (does not use float comparisons)
    * Works with -O3 -ffast-math (floating-point optimization)
    * Only call to standard library is memset and memcmp via <cstring>
    * Works for IEEE 754 compliant floating-point representations
    * Also works for extended precision long double
*/

#include <cstring>
template <class T> bool isNaN(T x)
{
  /*Initialize all bits including those used for alignment to zero. This sets
  all the values to positive zero but does not clue fast math optimizations as
  to the value of the variables.*/
  T z[4];
  memset(z, 0, sizeof(z));
  z[1] = -z[0];
  z[2] = x;
  z[3] = z[0] / z[2];

  /*Rationale for following test:
    * x is 0 or -0                                --> z[2] = 0, z[3] = NaN
    * x is a negative or positive number          --> z[3] = 0
    * x is a negative or positive denormal number --> z[3] = 0
    * x is negative or positive infinity          --> z[3] = 0
      (IEEE 754 guarantees that 0 / inf is zero)
    * x is a NaN                                  --> z[3] = NaN != 0.
  */

  //Do a bitwise comparison test for positive and negative zero.
  bool z2IsZero = memcmp(&z[2], &z[0], sizeof(T)) == 0 ||
                  memcmp(&z[2], &z[1], sizeof(T)) == 0;

  bool z3IsZero = memcmp(&z[3], &z[0], sizeof(T)) == 0 ||
                  memcmp(&z[3], &z[1], sizeof(T)) == 0; 

  //If the input is bitwise zero or negative zero, then it is not NaN.
  return !z2IsZero && !z3IsZero;
}

//NaN test suite
#include <iostream>

/*If printNaN is true then only expressions that are detected as NaN print and
vice versa.*/
template <class T> void test(bool printNaN)
{
  T v[10] = {-0.0, 0.0, -1.0, 1.0,
    std::numeric_limits<T>::infinity(),
    -std::numeric_limits<T>::infinity(),
    std::numeric_limits<T>::denorm_min(),
    -std::numeric_limits<T>::denorm_min(),
    std::numeric_limits<T>::quiet_NaN(),
    std::numeric_limits<T>::signaling_NaN()};
  for(int i = 0; i < 10; i++)
  {
    for(int j = 0; j < 10; j++)
    {
      if(isNaN(v[i] + v[j]) == printNaN)
        std::cout << v[i] << "+" << v[j] << " = " << v[i] + v[j] << std::endl;
      if(isNaN(v[i] - v[j]) == printNaN)
        std::cout << v[i] << "-" << v[j] << " = " << v[i] - v[j] << std::endl;
      if(isNaN(v[i] * v[j]) == printNaN)
        std::cout << v[i] << "*" << v[j] << " = " << v[i] * v[j] << std::endl;
      if(isNaN(v[i] / v[j]) == printNaN)
        std::cout << v[i] << "/" << v[j] << " = " << v[i] / v[j] << std::endl;
    }
  }
}

//Test each floating-point type.
int main()
{
  std::cout << "NaNs:" << std::endl;
  test<float>(true);
  test<double>(true);
  test<long double>(true);
  std::cout << std::endl << "Not NaNs:" << std::endl;
  test<float>(false);
  test<double>(false);
  test<long double>(false);
  return 0;
}
4
répondu William Andrew Burnson 2013-03-07 07:05:57

considérant que (x != x) n'est pas toujours garanti pour NaN( comme si on utilisait l'option-ffast-math), j'ai utilisé:

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

les nombres ne peuvent pas être à la fois < 0 et >= 0, donc en réalité cette vérification ne passe que si le nombre n'est ni inférieur, ni supérieur ou égal à zéro. Ce qui est en gros pas de numéro du tout, ou NaN.

vous pouvez également l'utiliser si vous préférez:

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

Je ne sais pas comment c'est affecté par-ffast-math cependant, de sorte que votre kilométrage peut varier.

4
répondu Jerramy 2016-01-26 01:06:53

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

3
répondu edW 2014-08-03 22:38:29

il me semble que la meilleure approche vraiment multiplateformes serait d'utiliser une union et de tester le modèle de bits du double pour vérifier les NaNs.

Je n'ai pas testé complètement cette solution, et il peut y avoir une façon 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;
}
1
répondu Sheldon Juncker 2016-10-08 17:20:07

la norme de L'IEEE dit lorsque l'exposant est tous les 1 et la mantisse n'est pas zéro, le numéro est un NaN. Le Double est un bit de signe, 11 bits d'exposant et 52 bits de mantissa. Faire un peu de vérification.

0
répondu bop 2014-01-31 19:26:31

comme l'indiquent les commentaires ci-dessus a != a ne fonctionnera pas dans g++ et d'autres compilateurs, mais cette astuce devrait. Ce n'est peut-être pas aussi efficace, mais c'est quand même un moyen:

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

fondamentalement, en g++ (Je ne suis pas sûr pour les autres cependant) printf imprime 'nan' sur %d ou %.F formats si la variable n'est pas un entier/flottant valide. Par conséquent, ce code vérifie que le premier caractère de la chaîne est 'n' (comme dans "nan")

-3
répondu ZenJ 2013-06-26 21:33:59