405 votes

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

Existe-t-il une fonction isnan() ?

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

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

2 votes

Je ne suis pas sûr qu'on puisse le faire de manière portative. Qui a dit que le C++ nécessite IEEE754 ?

0 votes

2 votes

Il faut savoir qu'une once de prévention vaut mieux qu'une livre de remèdes. En d'autres termes, empêchant 0.f/0.f d'être jamais exécuté est bien meilleure que la vérification rétroactive de nan dans votre code. nan peut être terriblement destructeur pour votre programme, si on le laisse proliférer, il peut introduire des bogues difficiles à trouver. Ceci est dû au fait que nan est toxique, (5* nan = nan ), nan n'est pas égal à quelque chose ( nan != nan ), nan pas plus grand que quoi que ce soit ( nan !> 0), nan n'est pas inférieur à quoi que ce soit ( nan !< 0).

386voto

jalf Points 142628

Selon la norme IEEE, les valeurs NaN ont la propriété étrange que les comparaisons qui les impliquent sont toujours faux. C'est-à-dire, pour un flotteur f, f != f sera vrai uniquement si f est NaN.

Notez que, comme certains commentaires ci-dessous l'ont souligné, tous les compilateurs ne respectent pas cela lorsqu'ils optimisent le code.

Pour tout compilateur qui prétend utiliser la virgule flottante IEEE, cette astuce devrait travail. Mais je ne peux pas garantir que ça sera le travail en pratique. En cas de doute, vérifiez auprès de votre compilateur.

0 votes

N'est-il pas possible que le compilateur l'optimise ? (Je ne sais pas car je n'ai pas utilisé le C++ depuis longtemps).

4 votes

Le compilateur a intérêt à ne pas supprimer cette option s'il fonctionne en mode IEEE. Vérifiez la documentation de votre compilateur, bien sûr...

0 votes

Le compilateur ne l'optimisera pas. La seule réserve est que cette propriété s'applique aux valeurs à virgule flottante IEEE, mais peut ne pas s'appliquer à d'autres formats. Les plates-formes courantes utilisent IEEE, donc le fonctionnement est garanti sur celles-ci, mais sur les plates-formes utilisant des formats de virgule flottante différents, cette astuce ne fonctionne pas.

228voto

mloskot Points 13971

Il n'y a pas isnan() disponible dans la bibliothèque standard C++ actuelle. Elle a été introduite dans 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 à C++. Malgré le fait qu'il n'a jamais été officiellement adopté pour devenir un standard C++, de nombreux ( GCC 4.0+. o Visual C++ 9.0 et plus Les implémentations C++ fournissent des fonctionnalités TR1, toutes ou seulement certaines (Visual C++ 9.0 ne fournit pas les 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:: bien que de nombreuses implémentations (par exemple, GCC 4+ sous Linux ou XCode sous Mac OS X 10.5+) les injectent directement dans l'espace de nom std:: donc std::isnan est bien défini.

De plus, certaines implémentations de C++ font encore du C99 isnan() disponible pour C++ (inclus par le biais de cmath o 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 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 la bibliothèque forme XCode, il semble (ici est 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 Power PC.

30 votes

Tout le monde veut ces fonctions comme isNan ou isInfinity. Pourquoi les responsables n'incluent-ils pas tout simplement dans leurs standards ????. - Je vais essayer de trouver comment devenir responsable et voter pour cela. Sérieusement.

14 votes

@shuhalo Déjà en charge ?

23 votes

Cette réponse doit être mise à jour depuis std::isnan fait maintenant partie de la norme C++11 et le support s'est étendu. std::isnan a été implémenté dans Visual Studio à partir de Visual Studio 2013. Peut-être que @shuhalo s'en est chargé :-)

215voto

BlueTrin Points 2130

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

Depuis que cette question a été posée, il y a eu quelques nouveaux développements : il faut 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 à virgule flottante donné arg n'est pas un nombre ( NaN ).

Paramètres

arg : valeur à virgule flottante

Valeur de retour

true si arg est NaN , false sinon

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, ceci est implémenté comme une macro isnan(c) qui renvoie une valeur int. Le type de x doit être un flottant, un double ou un double long.

Les différents vendeurs peuvent inclure ou non une fonction isnan() .

Le moyen supposé portable de vérifier NaN est d'utiliser la propriété IEEE 754 qui NaN n'est pas égal à lui-même : c'est-à-dire que 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 schéma binaire ...

9 votes

Mérite définitivement d'être la réponse acceptée et mérite plus de votes positifs. Merci pour le conseil

3 votes

1 std::isnan est toujours une mauvaise recommandation en février 2017, car il ne fonctionne pas avec l'optimisation en virgule flottante de g++.

0 votes

@Cheersandhth.-Alf : cette option est-elle conforme aux normes IEEE ? La réponse a été modifiée

82voto

Anonymous Points 11017

Il existe également un bibliothèque d'en-tête seulement présents dans Boost qui ont des outils soignés pour traiter les types de données en virgule flottante.

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

Vous bénéficiez des 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, jetez un coup d'œil à la boîte à outils Math de Boost, qui contient de nombreux outils utiles et qui se développe rapidement.

De même, lorsque vous traitez des points flottants et non flottants, il peut être judicieux de consulter l'outil de gestion des points. Conversions numériques .

1 votes

Merci ! C'est exactement ce que je cherchais.

0 votes

Il a été ajouté dans Boost 1.35 (je viens de découvrir que mon programme ne compile pas sur les anciennes distros linux).

2 votes

Si vous compilez avec l'option --fast-math, cette fonction ne fonctionnera pas comme prévu.

44voto

Il existe trois méthodes "officielles" : posix isnan macro , c++0x isnan modèle de fonction ou visual c++ _isnan función .

Malheureusement, il n'est pas très pratique de détecter laquelle de ces options utiliser.

Et malheureusement, il n'existe aucun moyen fiable de détecter si vous avez une représentation IEEE 754 avec des NaN. La bibliothèque standard offre un tel moyen officiel ( numeric_limits<double>::is_iec559 ). Mais en pratique, les compilateurs tels que g++ gâchent tout.

En théorie, on pourrait utiliser simplement x != x mais les compilateurs tels que g++ et visual c++ gâchent tout.

Donc, à la fin, testez pour le spécifique Modèles binaires NaN en supposant (et, espérons-le, en imposant, à un moment donné !) une représentation particulière telle que l'IEEE 754.


EDITAR pour illustrer le fait que les compilateurs tels que g++ font tout foirer, considérez les exemples suivants

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

Compilation 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> \_

4 votes

@Alf : Votre exemple fonctionne comme prévu pour moi sur Mac OS X et Linux avec différentes versions de g++ entre 4.0 et 4.5. La documentation de l -ffast-math indique explicitement qu'elle peut entraîner une sortie incorrecte pour les programmes qui dépendent d'une implémentation exacte des règles/spécifications IEEE ou ISO pour les fonctions mathématiques. Sans cette option, l'utilisation de x != x est un moyen parfaitement valide et portable de tester les NaN.

6 votes

@Adam : Ce que vous ne comprenez pas, c'est que la norme C++ ne requiert pas de représentation IEEE ou de calcul pour les flottants. D'après la page de manuel, gcc -ffast-math est toujours une implémentation C++ conforme (enfin, en supposant qu'il obtienne numeric_limits::is_iec559 C'est vrai, bien qu'Alf suggère ci-dessus que ce n'est pas le cas) : Le code C++ qui s'appuie sur IEEE est no C++ portable et n'a pas le droit d'attendre des implémentations qu'elles le fournissent.

5 votes

Et Alf a raison, un test rapide sur gcc 4.3.4 et is_iec559 est vrai avec -ffast-math . Le problème ici est que la documentation de GCC pour -ffast-math disent seulement qu'il est non-IEEE/ISO pour les fonctions mathématiques, alors qu'ils devrait dire qu'il n'est pas C++, parce que sa mise en œuvre de numeric_limits est borked. Je suppose que GCC n'est pas toujours en mesure de dire, au moment où le modèle est défini, si l'éventuel backend a effectivement des flottants conformes, et donc n'essaie même pas. IIRC il y a des problèmes similaires dans la liste des bogues en suspens pour la conformité de GCC à C99.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X