Juste une note, 1 once de prévention vaut mieux que 1 livre de cure. En d'autres termes, empêcher l'exécution de 0.f / 0.f est bien mieux que de vérifier rétroactivement nanles s dans votre code. nanCela peut être terriblement destructeur pour votre programme, s'il est autorisé à proliférer, il peut introduire des bogues difficiles à trouver. En effet, nanest toxique, (5 * nan= nan), nann'est égal à rien ( nan! = nan), nanNi supérieur à rien ( nan!> 0), nanni inférieur à rien ( nan! <0).
bobobobo
1
@bobobobo: c'est une fonctionnalité qui permet une vérification centralisée des erreurs. Tout comme les exceptions par rapport aux valeurs de retour.
Ben Voigt
2
Pourquoi <cmath> n'a-t-il pas isnan ()? C'est en std ::
frankliuao
Réponses:
351
Selon la norme IEEE, les valeurs NaN ont la propriété étrange que les comparaisons les impliquant sont toujours fausses. Autrement dit, pour un flottant f, f != fne sera vrai que si f est NaN.
Notez que, comme certains commentaires ci-dessous l'ont souligné, tous les compilateurs ne respectent pas cela lors de l'optimisation du code.
Pour tout compilateur qui prétend utiliser la virgule flottante IEEE, cette astuce devrait fonctionner. Mais je ne peux pas garantir que cela va fonctionner dans la pratique. Vérifiez auprès de votre compilateur en cas de doute.
Le compilateur ferait mieux de ne pas supprimer cela s'il fonctionne en mode IEEE. Consultez la documentation de votre compilateur, bien sûr ...
dmckee --- chaton ex-modérateur
38
-1 ne fonctionne qu'en théorie, pas en pratique: des compilateurs comme g ++ (avec -fastmath) bousillent ça. la seule façon générale, jusqu'à c ++ 0x, est de tester le bitpattern.
Bravo et hth. - Alf
66
@Alf: La documentation de l' -ffast-mathoption indique explicitement qu'elle peut entraîner une sortie incorrecte pour les programmes qui dépendent d'une implémentation exacte si les règles / spécifications IEEE ou ISO pour les fonctions mathématiques. Sans cette option activée, l'utilisation x != xest un moyen parfaitement valide et portable de tester NaN.
Adam Rosenfield
7
@Adam: la documentation indique ouvertement qu'elle n'est pas conforme, oui. et oui, j'ai déjà rencontré cet argument, en discutant longuement avec Gabriel Dos Reis. il est couramment utilisé pour défendre le design, dans un argument circulaire (je ne sais pas si vous aviez l'intention de vous y associer, mais il vaut la peine de le savoir - ce sont des trucs de guerre des flammes). votre conclusion x != xvalable sans cette option ne suit pas logiquement. cela peut être vrai pour une version particulière de g ++, ou non. de toute façon, vous n'avez généralement aucun moyen de garantir que l'option fastmath ne sera pas utilisée.
Bravo et hth. - Alf
7
@Alf: Non, je n'étais pas au courant de votre discussion avec Gabriel Dos Reis. Steve Jessop a fait un grand point dans l'autre question sur la prise en charge de la représentation IEEE. Si vous supposez IEEE 754 et que le compilateur fonctionne de manière conforme (c'est-à-dire sans l' -ffast-mathoption), alors x != xc'est une solution valide et portable. Vous pouvez même tester -ffast-mathen testant la __FAST_MATH__macro et basculer vers une implémentation différente dans ce cas (par exemple, utiliser les unions et le twiddling de bits).
Adam Rosenfield
220
Aucune isnan()fonction n'est disponible dans la bibliothèque standard C ++ actuelle. Il a été introduit en C99 et défini comme une macro et non comme une fonction. Les éléments de la bibliothèque standard définie par C99 ne font pas partie de la norme C ++ ISO / IEC 14882: 1998 actuelle 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 ++. En dépit du fait qu'il n'a jamais été officiellement adopté pour devenir la norme C ++, beaucoup ( les implémentations GCC 4.0+ ou Visual C ++ 9.0+ 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, cmathcomprend des éléments tels que C99 isnan(), isfinite()etc. , mais ils sont définis comme des fonctions, pas de macros, généralement dans l' std::tr1::espace de noms, bien que de nombreuses implémentations (ie gcc 4+ sous Linux ou dans Xcode sous Mac OS X 10.5+) Injecter les directement à std::, std::isnanest donc bien défini.
De plus, certaines implémentations de C ++ rendent toujours la isnan()macro C99 disponible pour C ++ (incluse via cmathou math.h), ce qui peut provoquer plus de confusions et les développeurs peuvent supposer que c'est un comportement standard.
Une note sur Viusal C ++, comme mentionné ci-dessus, il ne fournit pas non std::isnanplus std::tr1::isnan, mais il fournit une fonction d'extension définie comme _isnan()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 versions plus anciennes du compilateur et de la bibliothèque sous forme de XCode, il semble (voici une discussion pertinente ) que je n'ai pas eu la chance de me vérifier) deux fonctions sont définies, __inline_isnand()sur Intel et __isnand()sur Power PC.
Tout le monde veut ces fonctions comme isNan ou isInfinity. Pourquoi les responsables n'incluent-ils pas simplement dans leurs normes ???? - Je vais essayer de savoir comment me prendre en main et voter pour cela. Sérieusement.
shuhalo
8
@shuhalo Encore en charge?
Tomáš Zato - Reinstate Monica
11
Cette réponse devrait être mise à jour car elle std::isnanfait désormais 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 a pris en charge :-)
aberaud
170
Première solution: si vous utilisez C ++ 11
Depuis que cela a été demandé, il y a eu un peu de nouveaux développements: il est important de savoir que cela std::isnan()fait partie de C ++ 11
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, cela est implémenté comme une macro isnan(c)qui renvoie une valeur int. Le type de xdoit être float, double ou long double.
Divers fournisseurs peuvent ou non inclure ou non une fonction isnan().
La façon soi - disant portable pour vérifier NaNest d'utiliser la propriété IEEE 754 qui NaNne correspond pas à lui - même: à savoir x == xsera 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 ...
Mérite certainement d'être la réponse acceptée et mérite plus de votes positifs. Merci pour l'astuce
LBes
3
−1std::isnan est toujours une mauvaise recommandation en février 2017, car il ne fonctionne pas avec l'optimisation à virgule flottante de g ++.
Bravo et hth. - Alf
@ Cheersandhth.-Alf: cette option est-elle compatible IEEE? La réponse a été modifiée
BlueTrin
@BlueTrin: Les deux x != xet isnandoivent fonctionner pour la conformité IEEE 754. Concernant ce dernier, la norme IEEE 754-2008 stipule que «les implémentations doivent fournir les opérations non informatiques suivantes pour tous les formats arithmétiques pris en charge» et «isNaN (x) est vrai si et seulement si x est un NaN». Pour vérifier la conformité, cette norme exige is754version1985()et is754version2008(), où C ++ offre à la place std::numeric_limits<Fp>::is_iec559()(CEI 559 est la même norme). Malheureusement avec l' -ffast-mathoptimisation, par exemple, g ++ revendique la conformité mais n'est pas conforme.
Bravo et hth. - Alf
1
Avertissement: isnan (x) ne fonctionne pas avec l'option -ffinite-math-only dans gcc et clang
A Fog
82
Il y a aussi une bibliothèque uniquement en-tête présente dans Boost qui a des outils soignés pour gérer les types de données à virgule flottante
il a été ajouté dans Boost 1.35 (je viens de découvrir que mon programme ne compile pas sur l'ancienne distribution Linux).
marcin
2
si vous compilez avec l'option --fast-math alors cette fonction ne fonctionnera pas comme prévu.
Gaetano Mendola
43
Il y a trois façons "officielles": posix isnanmacro , c ++ 0x isnanmodèle de fonction , ou c ++ visuelle _isnanfonction .
Malheureusement, il est plutôt difficile de détecter lequel utiliser.
Et malheureusement, il n'existe aucun moyen fiable de détecter si vous avez une représentation IEEE 754 avec NaN. La bibliothèque standard propose une telle manière officielle ( numeric_limits<double>::is_iec559). Mais dans la pratique, les compilateurs tels que g ++ bousillent ça.
En théorie, on pourrait utiliser simplement x != x, mais des compilateurs tels que g ++ et visual c ++ bousillent ça.
Donc, à la fin, testez les modèles de bits NaN spécifiques , en supposant (et, espérons-le, en appliquant, à un moment donné!) Une représentation particulière telle que IEEE 754.
EDIT : comme exemple de "compilateurs tels que g ++… bousiller ça", considérons
#include<limits>#include<assert.h>void foo(double a,double b ){
assert( a != b );}int main(){typedef std::numeric_limits<double>Info;doubleconst nan1 =Info::quiet_NaN();doubleconst nan2 =Info::quiet_NaN();
foo( nan1, nan2 );}
Compilation avec g ++ (TDM-2 mingw32) 4.4.1:
C: \ test> tapez "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> un && écho fonctionne ... || écho! échoué
travaux...
C: \ test> gnuc x.cpp --fast-math
C: \ test> un && écho fonctionne ... || écho! échoué
Échec de l'assertion: a! = B, fichier x.cpp, ligne 6
Cette application a demandé au Runtime de la terminer de manière inhabituelle.
Veuillez contacter l'équipe d'assistance de l'application pour plus d'informations.
!échoué
C: \ test> _
@Alf: Votre exemple fonctionne comme prévu pour moi sur Mac OS X et Linux sur différentes versions de g ++ entre 4.0 et 4.5. La documentation de l' -ffast-mathoption indique explicitement qu'elle peut entraîner une sortie incorrecte pour les programmes qui dépendent d'une implémentation exacte si les règles / spécifications IEEE ou ISO pour les fonctions mathématiques. Sans cette option activée, l'utilisation x != xest un moyen parfaitement valide et portable de tester NaN.
Adam Rosenfield
6
@Adam: Ce qui vous manque, c'est que la norme C ++ ne nécessite pas de représentation IEEE ou de mathématiques pour les flottants. Pour autant que la page de manuel vous l'indique, il gcc -ffast-maths'agit toujours d'une implémentation C ++ conforme (enfin, en supposant que cela se passe numeric_limits::is_iec559bien, c'est le cas, bien que Alf suggère ci-dessus que non): le code C ++ reposant sur IEEE n'est pas C ++ portable et n'a pas le droit s'attendre à ce que les implémentations le fournissent.
Steve Jessop
5
Et Alf a raison, test rapide sur gcc 4.3.4 et is_iec559c'est vrai avec -ffast-math. Donc, le problème ici est que les documents de GCC -ffast-mathdisent seulement que ce n'est pas IEEE / ISO pour les fonctions mathématiques, alors qu'ils devraient dire que ce n'est pas C ++, car son implémentation de numeric_limitsest borked. Je suppose que GCC ne peut pas toujours dire au moment où le modèle est défini, si le backend éventuel a réellement 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é C99 de GCC.
Steve Jessop
1
@Alf, @Steve, je ne savais pas que la norme C ++ n'avait pas de spécification sur les valeurs à virgule flottante. C'est assez choquant pour moi. Il semble mieux gérer IEEE 754 et NaN comme une extension spécifique à la plate-forme plutôt que standard. N'est-ce pas? Et puis-je m'attendre à tout type d'isnan () ou IEEE754 ajouté en C ++ 0x?
Eonil
3
@Eonil: C ++ 0x a toujours par exemple "La représentation de la valeur des types à virgule flottante est définie par l'implémentation". C et C ++ visent tous deux à prendre en charge les implémentations sur des machines sans matériel à virgule flottante, et les flottants IEEE 754 appropriés peuvent être un peu plus lents à émuler que des alternatives raisonnablement précises. La théorie est que vous pouvez affirmer is_iec559si vous avez besoin d'IEEE, dans la pratique qui ne semble pas fonctionner sur GCC. C ++ 0x fait avoir une isnanfonction, mais étant donné que GCC ne met pas en œuvre correctement is_iec559maintenant, je suppose que ce ne sera pas en C ++ 0x soit, et -ffast-mathpourrait bien briser son isnan.
Steve Jessop
39
Il y a un std :: isnan si votre compilateur prend en charge les 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){volatiledouble d = var;return d != d;}
En faisant cela, il y a une chance que le compilateur optimise la comparaison, retournant toujours vrai.
CTT
23
Non, il n'y en a pas. Un compilateur qui fait cela est cassé. Autant dire qu'il y a une chance que la bibliothèque standard isnanrenvoie le mauvais résultat. Techniquement vrai, le compilateur pourrait être bogué, mais en pratique, ça ne va pas arriver. Identique à var != var. Cela fonctionne parce que c'est ainsi que les valeurs à virgule flottante IEEE sont définies.
2010
29
si -ffast-math est défini, isnan () ne retournera pas le résultat correct pour gcc. Bien sûr, cette optimisation est documentée comme brisant la sémantique IEEE ...
Matthew Herrmann
Si -ffast-math est défini, alors le compilateur est bogué. Ou plutôt, si -ffast-math est défini, tous les paris sont désactivés et vous ne pouvez pas compter sur NaN de toute façon.
Adrian Ratnapala
25
Vous pouvez utiliser numeric_limits<float>::quiet_NaN( )défini dans la limitsbibliothèque standard pour tester avec. Il y a une constante distincte définie pour double.
#include<iostream>#include<math.h>#include<limits>usingnamespace 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;}return0;}
Je ne sais pas si cela fonctionne sur toutes les plateformes, car je n'ai testé qu'avec g ++ sur Linux.
Attention, cependant - il semble y avoir un bogue dans numeric_limits dans GCC version 3.2.3, car il renvoie 0,0 pour quiet_NaN. D'après mon expérience, les versions ultérieures de GCC conviennent.
Nathan Kitchen
@Nathan: Bon à savoir. J'utilise la version 4.3.2, donc je suis bien sorti du bois.
Bill the Lizard
18
Vous pouvez utiliser la isnan()fonction, mais vous devez inclure la bibliothèque mathématique C.
#include<cmath>
Comme cette fonction fait partie de C99, elle n'est pas disponible partout. Si votre fournisseur ne fournit pas la fonction, vous pouvez également définir votre propre variante de compatibilité.
J'utilisais <cmath> et il n'y a aucun isnan dedans! soit dit en passant , j'ai découvert qu'il est un isnandans <math.h>
Hasen
1
Comme je l'ai dit, cela fait partie de C99. Comme C99 ne fait partie d'aucune norme C ++ actuelle, j'ai fourni l'alternative. Mais comme il est probable que isnan () soit inclus dans une prochaine norme C ++, j'ai mis une directive #ifndef autour.
raimue
12
Le code suivant utilise la définition de NAN (tous les bits d'exposants définis, au moins un ensemble de bits fractionnels) et suppose que sizeof (int) = sizeof (float) = 4. Vous pouvez rechercher NAN dans Wikipedia pour les détails.
Je pense que cela fonctionnerait également sur les grandes plateformes endiennes. Le littéral 0x7fffffffresterait simplement en mémoire ff ff ff 7f. valuea le même ordre que le fait 0x7f800000, donc toutes les opérations s'alignent (il n'y a pas d'échange d'octets). Je serais intéressé si quelqu'un pouvait tester cela sur une grande plateforme endienne.
Bryan W. Wagner
0x7fff1234est également un NaN. Il en est de même0xffffffff
Steve Hollasch
12
nan prévention
Ma réponse à cette question est de ne pas utiliser de chèques rétroactifs pournan . Utilisez plutôt des contrôles préventifs pour les 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.
nanrésulte de l'opération 0.f/0.f, ou 0.0/0.0. nanest un terrible ennemi de la stabilité de votre code qui doit être détecté et prévenu très soigneusement 1 . Les propriétés de nancela sont différentes des nombres normaux:
nanest toxique, (5 * nan= nan)
nann'est égal à rien, pas même à lui-même ( nan! = nan)
nanpas plus grand que tout ( nan!> 0)
nann'est pas moins que n'importe quoi ( nan! <0)
Les 2 dernières propriétés répertoriées sont contre-logiques et entraîneront un comportement étrange du code qui repose sur des comparaisons avec un nannombre (la 3ème dernière propriété est également étrange, mais vous n'allez probablement jamais voir x != x ?dans votre code (sauf si vous vérifiez pour nan (peu fiable))).
Dans mon propre code, j'ai remarqué que les nanvaleurs ont tendance à produire des bogues difficiles à trouver. (Notez que ce n'est pas le cas pour infou -inf. ( -inf<0) renvoie TRUE, (0 < inf) retourne VRAI et même ( -inf< inf) retourne VRAI. Ainsi, selon mon expérience, le comportement du code est souvent toujours comme souhaité).
que faire sous nan
Ce que vous voulez faire 0.0/0.0doit être traité comme un cas spécial , mais ce que vous faites doit dépendre des nombres que vous attendez du code.
Dans l'exemple ci-dessus, le résultat de ( 0.f/FLT_MIN) sera 0, fondamentalement. Vous voudrez peut- 0.0/0.0être générer à la HUGEplace. Donc,
float x=0.f, y=0.f, z;if(!x &&!y )// 0.f/0.f case
z = FLT_MAX ;// biggest float possibleelse
z = y/x ;// regular division.
Ainsi , dans ce qui précède, si X 0.f, infaurait pour résultat (qui a très bon / comportement non destructif comme mentionné ci - dessus en fait).
N'oubliez pas que la division entière 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 l' 0.0/0.0évaluation est silencieuse nanque vous pouvez être paresseux et ne pas vérifier 0.0/0.0avant que cela ne se produise.
1 Les vérifications de nanvia x != xsont parfois peu fiables ( x != xétant supprimées par certains compilateurs d'optimisation qui ne respectent pas la conformité IEEE, en particulier lorsque le -ffast-mathcommutateur est activé).
Merci de l'avoir signalé; une telle programmation aiderait certainement à résoudre le problème en tant que tel. Mais la prochaine fois, essayez de ne pas trop abuser des fonctionnalités de mise en forme du texte. Changer de taille de police, d'épaisseur et de style comme ça rend la lecture très difficile.
Magnus
4
Notez que 0,0 / 0,0 n'est pas la seule opération qui pourrait entraîner un NaN. La racine carrée d'un nombre négatif renvoie NaN. Le cosinus de + infini renvoie également NaN. l'opération acos (x) où x n'est pas dans la plage [0, pi] peut également entraîner NaN. En un mot, il faut être extrêmement prudent pour examiner également ces opérations potentiellement risquées, non seulement à 0,0 / 0,0.
Boris Dalstein,
Tout à fait d'accord avec Boris. D'après mon expérience, NaN provient presque toujours de quelque chose comme sqrt (-1.302e-53), c'est-à-dire que les résultats de calcul intermédiaires proches de zéro sont introduits dans sqrt sans vérifier la négativité.
hans_meine
1
"Prévenir les NaN" signifie que vous devez entrer dans toutes les opérations arithmétiques de base, pas seulement dans la division. Vous devrez faire attention à ∞ / ∞, 0 * ∞, ∞% x, x% 0, ∞ - ∞, 0 ^ 0, ∞ ^ 0, entre autres. Être "préventif" avec de telles opérations arithmétiques de base signifie que vous aurez complètement votre performance (et raterez probablement des cas supplémentaires auxquels vous n'aviez pas pensé).
Steve Hollasch
11
Depuis C ++ 14, il existe plusieurs façons de tester si un nombre à virgule flottante valueest un NaN.
De ces façons, seule la vérification des bits de la représentation du nombre fonctionne de manière fiable, comme indiqué dans ma réponse d'origine. En particulier, std::isnanet la vérification souvent proposée v != v, ne fonctionne pas de manière fiable et ne doit pas être utilisée, de peur que votre code cesse de fonctionner correctement lorsque quelqu'un décide que l'optimisation en virgule flottante est nécessaire et demande au compilateur de le faire. Cette situation peut changer, les compilateurs peuvent devenir plus conformes, mais pour ce problème qui ne s'est pas produit au cours des 6 années écoulées depuis la réponse d'origine.
Pendant environ 6 ans, ma réponse initiale était la solution choisie pour cette question, qui était OK. Mais récemment, une réponse très appréciée recommandant le v != vtest non fiable a été sélectionnée. D'où cette réponse encore plus à jour (nous avons désormais les standards C ++ 11 et C ++ 14, et C ++ 17 à l'horizon).
Les principaux moyens de vérifier la non-NaN, à partir de C ++ 14, sont les suivants:
std::isnan(value) )
est le moyen de bibliothèque standard prévu depuis C ++ 11. isnanapparemment en conflit avec la macro Posix du même nom, mais en pratique ce n'est pas un problème. Le problème principal est que lorsque l'optimisation arithmétique à virgule flottante est demandée, alors avec au moins un compilateur principal, à savoir g ++, std::isnanrenvoie l' falseargument NaN .
(fpclassify(value) == FP_NAN) )
Souffre du même problème std::isnan, c'est-à-dire n'est pas fiable.
(value != value) )
Recommandé dans de nombreuses réponses SO. Souffre du même problème std::isnan, c'est-à-dire n'est pas fiable.
(value == Fp_info::quiet_NaN()) )
Il s'agit d'un test qui, avec un comportement standard, ne devrait pas détecter les NaN, mais qui, avec le comportement optimisé, pourrait détecter les NaN (en raison du code optimisé comparant directement les représentations de niveau de bit), 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 std::isnan, c'est-à-dire n'est pas fiable.
isunordered(1.2345, value) )
Souffre du même problème 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 la norme IEEE 754. Il est complètement fiable mais le code dépend quelque peu du système.
Dans le code de test complet suivant, «succès» consiste à savoir si une expression signale Nan-ness de la valeur. Pour la plupart des expressions, cette mesure de succès, l'objectif de détecter les NaN et uniquement les NaN, correspond à leur sémantique standard. Pour l' (value == Fp_info::quiet_NaN()) )expression, cependant, le comportement standard est qu'il ne fonctionne pas comme un détecteur NaN.
Résultats avec g ++ (notez à nouveau que le comportement standard de (value == Fp_info::quiet_NaN())est qu'il ne fonctionne pas comme un détecteur NaN, c'est juste très intéressant ici):
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> g ++ --version | trouver "++"
g ++ (x86_64-win32-sjlj-rev1, construit par le projet MinGW-W64) 6.3.0
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> g ++ foo.cpp && a
Le compilateur revendique IEEE 754 = true
v = nan, (std :: isnan (valeur)) = vrai succès
u = 3,14, (std :: isnan (valeur)) = faux succès
w = inf, (std :: isnan (value)) = false Réussite
v = nan, ((fpclassify (value) == 0x0100)) = true Success
u = 3,14, ((fpclassify (value) == 0x0100)) = false Success
w = inf, ((fpclassify (value) == 0x0100)) = false Success
v = nan, ((valeur! = valeur)) = vrai succès
u = 3,14, ((valeur! = valeur)) = faux succès
w = inf, ((valeur! = valeur)) = faux Succès
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 (valeur) == ((int) 0x80000000))) = vrai succès
u = 3,14, ((ilogb (valeur) == ((int) 0x80000000))) = faux succès
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)) = vrai succès
u = 3,14, (is_ieee754_nan (valeur)) = faux succès
w = inf, (is_ieee754_nan (value)) = false Success
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> g ++ foo.cpp -fast-math && a
Le compilateur revendique IEEE 754 = true
v = nan, (std :: isnan (valeur)) = false ÉCHEC
u = 3,14, (std :: isnan (valeur)) = faux succès
w = inf, (std :: isnan (value)) = false Réussite
v = nan, ((fpclassify (value) == 0x0100)) = false FAILED
u = 3,14, ((fpclassify (value) == 0x0100)) = false Success
w = inf, ((fpclassify (value) == 0x0100)) = false Success
v = nan, ((valeur! = valeur)) = faux ÉCHEC
u = 3,14, ((valeur! = valeur)) = faux succès
w = inf, ((valeur! = valeur)) = faux Succès
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 (valeur) == ((int) 0x80000000))) = vrai succès
u = 3,14, ((ilogb (valeur) == ((int) 0x80000000))) = faux succès
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)) = vrai succès
u = 3,14, (is_ieee754_nan (valeur)) = faux succès
w = inf, (is_ieee754_nan (value)) = false Success
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> _
Résultats avec Visual C ++:
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> cl / nologo- 2> & 1 | trouver "++"
Microsoft (R) C / C ++ Optimizing Compiler Version 19.00.23725 pour x86
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> cl foo.cpp / Feb && b
foo.cpp
Le compilateur revendique IEEE 754 = true
v = nan, (std :: isnan (valeur)) = vrai succès
u = 3,14, (std :: isnan (valeur)) = faux succès
w = inf, (std :: isnan (value)) = false Réussite
v = nan, ((fpclassify (value) == 2)) = true Success
u = 3,14, ((fpclassify (value) == 2)) = false Success
w = inf, ((fpclassify (value) == 2)) = false Success
v = nan, ((valeur! = valeur)) = vrai succès
u = 3,14, ((valeur! = valeur)) = faux succès
w = inf, ((valeur! = valeur)) = faux Succès
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 (valeur) == 0x7fffffff)) = vrai succès
u = 3,14, ((ilogb (valeur) == 0x7fffffff)) = faux succès
w = inf, ((ilogb (valeur) == 0x7fffffff)) = true ÉCHEC
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)) = vrai succès
u = 3,14, (is_ieee754_nan (valeur)) = faux succès
w = inf, (is_ieee754_nan (value)) = false Success
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> cl foo.cpp / Feb / fp: rapide && b
foo.cpp
Le compilateur revendique IEEE 754 = true
v = nan, (std :: isnan (valeur)) = vrai succès
u = 3,14, (std :: isnan (valeur)) = faux succès
w = inf, (std :: isnan (value)) = false Réussite
v = nan, ((fpclassify (value) == 2)) = true Success
u = 3,14, ((fpclassify (value) == 2)) = false Success
w = inf, ((fpclassify (value) == 2)) = false Success
v = nan, ((valeur! = valeur)) = vrai succès
u = 3,14, ((valeur! = valeur)) = faux succès
w = inf, ((valeur! = valeur)) = faux Succès
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 (valeur) == 0x7fffffff)) = vrai succès
u = 3,14, ((ilogb (valeur) == 0x7fffffff)) = faux succès
w = inf, ((ilogb (valeur) == 0x7fffffff)) = true ÉCHEC
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)) = vrai succès
u = 3,14, (is_ieee754_nan (valeur)) = faux succès
w = inf, (is_ieee754_nan (value)) = false Success
[C: \ my \ forums \ so \ 282 (détecter NaN)]
> _
Pour résumer les résultats ci-dessus, seuls les tests directs de la représentation au niveau du bit, en utilisant la is_ieee754_nanfonction définie dans ce programme de test, ont fonctionné de manière fiable dans tous les cas avec g ++ et Visual C ++.
Addendum:
Après avoir publié ce qui précède, j'ai pris connaissance d'un autre test possible pour NaN, mentionné dans une autre réponse ici, à savoir ((value < 0) == (value >= 0)). Cela s'est avéré fonctionner correctement avec Visual C ++ mais a échoué avec l' -ffast-mathoption de g ++ . Seuls les tests Bitpattern directs fonctionnent de manière fiable.
inlineboolIsNan(float f){constuint32 u =*(uint32*)&f;return(u&0x7F800000)==0x7F800000&&(u&0x7FFFFF);// Both NaN and qNan.}inlineboolIsNan(double d){constuint64 u =*(uint64*)&d;return(u&0x7FF0000000000000ULL)==0x7FF0000000000000ULL&&(u&0xFFFFFFFFFFFFFULL);}
Cela fonctionne si sizeof(int)est 4 et sizeof(long long)est 8.
Pendant l'exécution, ce n'est qu'une comparaison, les castings ne prennent pas de temps. Il modifie simplement la configuration des drapeaux de comparaison pour vérifier l'égalité.
Notez également qu'il est limité à la représentation IEEE 754.
Bravo et hth. - Alf
Notez que ce transtypage rompt la règle d'aliasing stricte de g ++, et que le compilateur est connu pour faire Unmentionable Things ™ lorsqu'il détecte l'UB formelle. Au lieu de conversions efficaces, avec g ++ vous devez utiliser memcpy, à travers un tableau d'octets pour être sûr. Code pour cela dans ma réponse n ° 2 .
Bravo et hth. - Alf
4
Une solution possible qui ne dépendrait pas de la représentation IEEE spécifique pour NaN utilisée serait la suivante:
template<class T>bool isnan( T f ){
T _nan =(T)0.0/(T)0.0;return0== memcmp((void*)&f,(void*)&_nan,sizeof(T));}
La virgule flottante simple précision a plus de 8 millions de représentations de bits légitimes et différentes pour NaN, vous devrez donc ajouter quelques comparaisons supplémentaires. :)
Steve Hollasch
4
Étant donné que (x! = X) n'est pas toujours garanti pour NaN (comme si vous utilisez 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 cette vérification n'est réussie que si le nombre n'est ni inférieur, ni supérieur ou égal à zéro. Ce qui est fondamentalement pas de nombre 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 cela est affecté par -ffast-math, donc votre kilométrage peut varier.
Ceci est en fait imparfait de la même manière qu'il l' f != fest aussi. J'ai vu llvm optimiser un morceau de code presque identique. L'optimiseur peut propager les informations sur la première comparaison et comprendre que la deuxième comparaison peut ne jamais être vraie si la première l'est. (si le compilateur obéit strictement aux règles IEEE f != fest de toute façon beaucoup plus simple)
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. Elle repose sur le fait que le seul cas où une valeur n'est pas égale à elle-même est lorsque la valeur n'est pas un nombre.
Il me semble que la meilleure approche véritablement multiplateforme serait d'utiliser une union et de tester la configuration binaire du double pour vérifier les NaN.
Je n'ai pas testé cette solution en profondeur, et il peut y avoir une façon plus efficace de travailler avec les modèles de bits, mais je pense que cela devrait fonctionner.
#include<stdint.h>#include<stdio.h>unionNaN{uint64_t bits;double num;};int main(){//Test if a double is NaNdouble d =0.0/0.0;unionNaN n;
n.num = d;if((n.bits |0x800FFFFFFFFFFFFF)==0xFFFFFFFFFFFFFFFF){
printf("NaN: %f", d);}return0;}
Notez que "c'est un comportement indéfini à lire du membre du syndicat qui n'a pas été écrit le plus récemment". Par conséquent, cette utilisation d'un uniontype de jeu de mots entre deux types peut ne pas fonctionner comme souhaité (: sad_panda :). La bonne façon (bien que pas aussi portable que souhaité) serait d'éviter complètement l'union et de faire un memcpy de doubledans une uint64_tvariable différente , puis de faire le test en utilisant cette variable auxiliaire.
Eljay
0
Sur x86-64, vous pouvez avoir des méthodes extrêmement rapides pour vérifier NaN et infini, qui fonctionnent indépendamment de l' -ffast-mathoption du compilateur. ( f != f, std::isnan, std::isinfCédez toujours falseavec -ffast-math).
Les tests pour NaN, l'infini et les nombres finis peuvent facilement être effectués en vérifiant l'exposant maximum. l'infini est l'exposant maximum avec une mantisse nulle, NaN est l'exposant maximum et une mantisse non nulle. L'exposant est stocké dans les bits suivants après le bit de signe le plus haut, afin que nous puissions simplement déplacer à gauche pour se débarrasser du bit de signe et faire de l'exposant les bits du haut, aucun masquage ( operator&) n'est nécessaire:
staticinlineuint64_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;}staticinlineuint32_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;}constexpruint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);constexpruint32_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.staticinlinebool isnan2(double a){return load_ieee754_rep(a)<<1> inf_double_shl1;}staticinlinebool isinf2(double a){return load_ieee754_rep(a)<<1== inf_double_shl1;}staticinlinebool isfinite2(double a){return load_ieee754_rep(a)<<1< inf_double_shl1;}staticinlinebool isnan2(float a){return load_ieee754_rep(a)<<1> inf_float_shl1;}staticinlinebool isinf2(float a){return load_ieee754_rep(a)<<1== inf_float_shl1;}staticinlinebool isfinite2(float a){return load_ieee754_rep(a)<<1< inf_float_shl1;}
Les stdversions de isinfet isfinitechargent 2 double/floatconstantes à partir du .datasegment et dans le pire des cas, elles peuvent provoquer 2 échecs de cache de données. Les versions ci-dessus ne chargent aucune donnée inf_double_shl1et les inf_float_shl1constantes sont encodées en tant qu'opérandes immédiats dans les instructions d'assemblage.
Plus rapide isnan2est juste 2 instructions de montage:
Utilise le fait que l' ucomisdinstruction définit l'indicateur de parité si un argument est NaN. C'est ainsi que std::isnanfonctionne lorsqu'aucune -ffast-mathoption n'est spécifiée.
La norme IEEE dit que lorsque l'exposant est tout 1s et que la mantisse n'est pas nulle, le nombre est a NaN. Le double est le 1bit de signe, 11les bits d'exposant et les 52bits de mantisse. Vérifiez un peu.
Comme les commentaires ci-dessus indiquent a! = A ne fonctionnera pas dans g ++ et certains autres compilateurs, mais cette astuce devrait. Ce n'est peut-être pas aussi efficace, mais c'est quand même un moyen:
Fondamentalement, dans g ++ (je ne suis pas sûr pour les autres cependant) printf affiche 'nan' aux formats% d ou% .f 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»)
Cela ne provoquerait-il pas un débordement de tampon si a = 234324.0f?
Mazyod
Oui tu le veux, ou 340282346638528859811704183484516925440.000si a = FLT_MAX. Il devrait utiliser char s[7]; sprintf(s, "%.0g", a);, qui sera de 6 heures si a=-FLT_MAX, ou-3e+38
bobobobo
-3
Cela détecte l'infini et également NaN dans Visual Studio en vérifiant qu'il est dans les doubles limites:
//#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;
Vérifiez la définition de FLT_MIN, DBL_MINet LDBL_MINplus attentivement. Ces valeurs sont définies comme étant les plus petites valeurs normalisées pour chaque type. Par exemple, la simple précision a plus de 8 millions de valeurs de denorm légitimes qui sont supérieures à zéro et inférieures à FLT_MIN(et ne sont pas NaN).
nan
les s dans votre code.nan
Cela peut être terriblement destructeur pour votre programme, s'il est autorisé à proliférer, il peut introduire des bogues difficiles à trouver. En effet,nan
est toxique, (5 *nan
=nan
),nan
n'est égal à rien (nan
! =nan
),nan
Ni supérieur à rien (nan
!> 0),nan
ni inférieur à rien (nan
! <0).Réponses:
Selon la norme IEEE, les valeurs NaN ont la propriété étrange que les comparaisons les impliquant sont toujours fausses. Autrement dit, pour un flottant f,
f != f
ne sera vrai que si f est NaN.Notez que, comme certains commentaires ci-dessous l'ont souligné, tous les compilateurs ne respectent pas cela lors de l'optimisation du code.
Pour tout compilateur qui prétend utiliser la virgule flottante IEEE, cette astuce devrait fonctionner. Mais je ne peux pas garantir que cela va fonctionner dans la pratique. Vérifiez auprès de votre compilateur en cas de doute.
la source
-ffast-math
option indique explicitement qu'elle peut entraîner une sortie incorrecte pour les programmes qui dépendent d'une implémentation exacte si les règles / spécifications IEEE ou ISO pour les fonctions mathématiques. Sans cette option activée, l'utilisationx != x
est un moyen parfaitement valide et portable de tester NaN.x != x
valable sans cette option ne suit pas logiquement. cela peut être vrai pour une version particulière de g ++, ou non. de toute façon, vous n'avez généralement aucun moyen de garantir que l'option fastmath ne sera pas utilisée.-ffast-math
option), alorsx != x
c'est une solution valide et portable. Vous pouvez même tester-ffast-math
en testant la__FAST_MATH__
macro et basculer vers une implémentation différente dans ce cas (par exemple, utiliser les unions et le twiddling de bits).Aucune
isnan()
fonction n'est disponible dans la bibliothèque standard C ++ actuelle. Il a été introduit en C99 et défini comme une macro et non comme une fonction. Les éléments de la bibliothèque standard définie par C99 ne font pas partie de la norme C ++ ISO / IEC 14882: 1998 actuelle 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 ++. En dépit du fait qu'il n'a jamais été officiellement adopté pour devenir la norme C ++, beaucoup ( les implémentations GCC 4.0+ ou Visual C ++ 9.0+ 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,
cmath
comprend des éléments tels que C99isnan()
,isfinite()
etc. , mais ils sont définis comme des fonctions, pas de macros, généralement dans l'std::tr1::
espace de noms, bien que de nombreuses implémentations (ie gcc 4+ sous Linux ou dans Xcode sous Mac OS X 10.5+) Injecter les directement àstd::
,std::isnan
est donc bien défini.De plus, certaines implémentations de C ++ rendent toujours la
isnan()
macro C99 disponible pour C ++ (incluse viacmath
oumath.h
), ce qui peut provoquer plus de confusions et les développeurs peuvent supposer que c'est un comportement standard.Une note sur Viusal C ++, comme mentionné ci-dessus, il ne fournit pas non
std::isnan
plusstd::tr1::isnan
, mais il fournit une fonction d'extension définie comme_isnan()
disponible depuis Visual C ++ 6.0Sur 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 sous forme de XCode, il semble (voici une discussion pertinente ) que je n'ai pas eu la chance de me vérifier) deux fonctions sont définies,__inline_isnand()
sur Intel et__isnand()
sur Power PC.la source
std::isnan
fait désormais 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 a pris en charge :-)Première solution: si vous utilisez C ++ 11
Depuis que cela a été demandé, il y a eu un peu de nouveaux développements: il est important de savoir que cela
std::isnan()
fait partie de C ++ 11Synopsis
Défini dans l'en-tête
<cmath>
Détermine si le nombre à virgule flottante donné arg n'est pas un nombre (
NaN
).Paramètres
arg
: valeur en virgule flottanteValeur de retour
true
si arg l'estNaN
,false
sinonRé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, cela est implémenté comme une macro
isnan(c)
qui renvoie une valeur int. Le type dex
doit être float, double ou long double.Divers fournisseurs peuvent ou non inclure ou non une fonction
isnan()
.La façon soi - disant portable pour vérifier
NaN
est d'utiliser la propriété IEEE 754 quiNaN
ne correspond pas à lui - même: à savoirx == x
sera faux pourx
êtreNaN
.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 ...
la source
std::isnan
est toujours une mauvaise recommandation en février 2017, car il ne fonctionne pas avec l'optimisation à virgule flottante de g ++.x != x
etisnan
doivent fonctionner pour la conformité IEEE 754. Concernant ce dernier, la norme IEEE 754-2008 stipule que «les implémentations doivent fournir les opérations non informatiques suivantes pour tous les formats arithmétiques pris en charge» et «isNaN (x) est vrai si et seulement si x est un NaN». Pour vérifier la conformité, cette norme exigeis754version1985()
etis754version2008()
, où C ++ offre à la placestd::numeric_limits<Fp>::is_iec559()
(CEI 559 est la même norme). Malheureusement avec l'-ffast-math
optimisation, par exemple, g ++ revendique la conformité mais n'est pas conforme.Il y a aussi une bibliothèque uniquement en-tête présente dans Boost qui a des outils soignés pour gérer les types de données à virgule flottante
Vous obtenez les fonctions suivantes:
Si vous avez le temps, jetez un œil à la boîte à outils Math complète de Boost, elle contient de nombreux outils utiles et se développe rapidement.
De plus, lorsqu'il s'agit de points flottants et non flottants, il peut être judicieux d'examiner les conversions numériques .
la source
Il y a trois façons "officielles": posix
isnan
macro , c ++ 0xisnan
modèle de fonction , ou c ++ visuelle_isnan
fonction .Malheureusement, il est plutôt difficile de détecter lequel utiliser.
Et malheureusement, il n'existe aucun moyen fiable de détecter si vous avez une représentation IEEE 754 avec NaN. La bibliothèque standard propose une telle manière officielle (
numeric_limits<double>::is_iec559
). Mais dans la pratique, les compilateurs tels que g ++ bousillent ça.En théorie, on pourrait utiliser simplement
x != x
, mais des compilateurs tels que g ++ et visual c ++ bousillent ça.Donc, à la fin, testez les modèles de bits NaN spécifiques , en supposant (et, espérons-le, en appliquant, à un moment donné!) Une représentation particulière telle que IEEE 754.
EDIT : comme exemple de "compilateurs tels que g ++… bousiller ça", considérons
Compilation avec g ++ (TDM-2 mingw32) 4.4.1:
la source
-ffast-math
option indique explicitement qu'elle peut entraîner une sortie incorrecte pour les programmes qui dépendent d'une implémentation exacte si les règles / spécifications IEEE ou ISO pour les fonctions mathématiques. Sans cette option activée, l'utilisationx != x
est un moyen parfaitement valide et portable de tester NaN.gcc -ffast-math
s'agit toujours d'une implémentation C ++ conforme (enfin, en supposant que cela se passenumeric_limits::is_iec559
bien, c'est le cas, bien que Alf suggère ci-dessus que non): le code C ++ reposant sur IEEE n'est pas C ++ portable et n'a pas le droit s'attendre à ce que les implémentations le fournissent.is_iec559
c'est vrai avec-ffast-math
. Donc, le problème ici est que les documents de GCC-ffast-math
disent seulement que ce n'est pas IEEE / ISO pour les fonctions mathématiques, alors qu'ils devraient dire que ce n'est pas C ++, car son implémentation denumeric_limits
est borked. Je suppose que GCC ne peut pas toujours dire au moment où le modèle est défini, si le backend éventuel a réellement 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é C99 de GCC.is_iec559
si vous avez besoin d'IEEE, dans la pratique qui ne semble pas fonctionner sur GCC. C ++ 0x fait avoir uneisnan
fonction, mais étant donné que GCC ne met pas en œuvre correctementis_iec559
maintenant, je suppose que ce ne sera pas en C ++ 0x soit, et-ffast-math
pourrait bien briser sonisnan
.Il y a un std :: isnan si votre compilateur prend en charge les 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:
la source
isnan
renvoie le mauvais résultat. Techniquement vrai, le compilateur pourrait être bogué, mais en pratique, ça ne va pas arriver. Identique àvar != var
. Cela fonctionne parce que c'est ainsi que les valeurs à virgule flottante IEEE sont définies.Vous pouvez utiliser
numeric_limits<float>::quiet_NaN( )
défini dans lalimits
bibliothèque standard pour tester avec. Il y a une constante distincte définie pourdouble
.Je ne sais pas si cela fonctionne sur toutes les plateformes, car je n'ai testé qu'avec g ++ sur Linux.
la source
Vous pouvez utiliser la
isnan()
fonction, mais vous devez inclure la bibliothèque mathématique C.Comme cette fonction fait partie de C99, elle n'est pas disponible partout. Si votre fournisseur ne fournit pas la fonction, vous pouvez également définir votre propre variante de compatibilité.
la source
isnan
dans <math.h>Le code suivant utilise la définition de NAN (tous les bits d'exposants définis, au moins un ensemble de bits fractionnels) et suppose que sizeof (int) = sizeof (float) = 4. Vous pouvez rechercher NAN dans Wikipedia pour les détails.
bool IsNan( float value ) { return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000; }
la source
0x7fffffff
resterait simplement en mémoireff ff ff 7f
.value
a le même ordre que le fait0x7f800000
, donc toutes les opérations s'alignent (il n'y a pas d'échange d'octets). Je serais intéressé si quelqu'un pouvait tester cela sur une grande plateforme endienne.0x7fff1234
est également un NaN. Il en est de même0xffffffff
nan prévention
Ma réponse à cette question est de ne pas utiliser de chèques rétroactifs pour
nan
. Utilisez plutôt des contrôles préventifs pour les divisions du formulaire0.0/0.0
.nan
résulte de l'opération0.f/0.f
, ou0.0/0.0
.nan
est un terrible ennemi de la stabilité de votre code qui doit être détecté et prévenu très soigneusement 1 . Les propriétés denan
cela sont différentes des nombres normaux:nan
est toxique, (5 *nan
=nan
)nan
n'est égal à rien, pas même à lui-même (nan
! =nan
)nan
pas plus grand que tout (nan
!> 0)nan
n'est pas moins que n'importe quoi (nan
! <0)Les 2 dernières propriétés répertoriées sont contre-logiques et entraîneront un comportement étrange du code qui repose sur des comparaisons avec un
nan
nombre (la 3ème dernière propriété est également étrange, mais vous n'allez probablement jamais voirx != x ?
dans votre code (sauf si vous vérifiez pour nan (peu fiable))).Dans mon propre code, j'ai remarqué que les
nan
valeurs ont tendance à produire des bogues difficiles à trouver. (Notez que ce n'est pas le cas pourinf
ou-inf
. (-inf
<0) renvoieTRUE
, (0 <inf
) retourne VRAI et même (-inf
<inf
) retourne VRAI. Ainsi, selon mon expérience, le comportement du code est souvent toujours comme souhaité).que faire sous nan
Ce que vous voulez faire
0.0/0.0
doit être traité comme un cas spécial , mais ce que vous faites doit dépendre des nombres que vous attendez du code.Dans l'exemple ci-dessus, le résultat de (
0.f/FLT_MIN
) sera0
, fondamentalement. Vous voudrez peut-0.0/0.0
être générer à laHUGE
place. Donc,Ainsi , dans ce qui précède, si X
0.f
,inf
aurait pour résultat (qui a très bon / comportement non destructif comme mentionné ci - dessus en fait).N'oubliez pas que la division entière 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 l'
0.0/0.0
évaluation est silencieusenan
que vous pouvez être paresseux et ne pas vérifier0.0/0.0
avant que cela ne se produise.1 Les vérifications de
nan
viax != x
sont parfois peu fiables (x != x
étant supprimées par certains compilateurs d'optimisation qui ne respectent pas la conformité IEEE, en particulier lorsque le-ffast-math
commutateur est activé).la source
Depuis C ++ 14, il existe plusieurs façons de tester si un nombre à virgule flottante
value
est un NaN.De ces façons, seule la vérification des bits de la représentation du nombre fonctionne de manière fiable, comme indiqué dans ma réponse d'origine. En particulier,
std::isnan
et la vérification souvent proposéev != v
, ne fonctionne pas de manière fiable et ne doit pas être utilisée, de peur que votre code cesse de fonctionner correctement lorsque quelqu'un décide que l'optimisation en virgule flottante est nécessaire et demande au compilateur de le faire. Cette situation peut changer, les compilateurs peuvent devenir plus conformes, mais pour ce problème qui ne s'est pas produit au cours des 6 années écoulées depuis la réponse d'origine.Pendant environ 6 ans, ma réponse initiale était la solution choisie pour cette question, qui était OK. Mais récemment, une réponse très appréciée recommandant le
v != v
test non fiable a été sélectionnée. D'où cette réponse encore plus à jour (nous avons désormais les standards C ++ 11 et C ++ 14, et C ++ 17 à l'horizon).Les principaux moyens de vérifier la non-NaN, à partir de C ++ 14, sont les suivants:
std::isnan(value) )
est le moyen de bibliothèque standard prévu depuis C ++ 11.
isnan
apparemment en conflit avec la macro Posix du même nom, mais en pratique ce n'est pas un problème. Le problème principal est que lorsque l'optimisation arithmétique à virgule flottante est demandée, alors avec au moins un compilateur principal, à savoir g ++,std::isnan
renvoie l'false
argument NaN .(fpclassify(value) == FP_NAN) )
Souffre du même problème
std::isnan
, c'est-à-dire n'est pas fiable.(value != value) )
Recommandé dans de nombreuses réponses SO. Souffre du même problème
std::isnan
, c'est-à-dire n'est pas fiable.(value == Fp_info::quiet_NaN()) )
Il s'agit d'un test qui, avec un comportement standard, ne devrait pas détecter les NaN, mais qui, avec le comportement optimisé, pourrait détecter les NaN (en raison du code optimisé comparant directement les représentations de niveau de bit), 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
std::isnan
, c'est-à-dire n'est pas fiable.isunordered(1.2345, value) )
Souffre du même problème
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 la norme IEEE 754. Il est complètement fiable mais le code dépend quelque peu du système.
Dans le code de test complet suivant, «succès» consiste à savoir si une expression signale Nan-ness de la valeur. Pour la plupart des expressions, cette mesure de succès, l'objectif de détecter les NaN et uniquement les NaN, correspond à leur sémantique standard. Pour l'
(value == Fp_info::quiet_NaN()) )
expression, cependant, le comportement standard est qu'il ne fonctionne pas comme un détecteur NaN.Résultats avec g ++ (notez à nouveau que le comportement standard de
(value == Fp_info::quiet_NaN())
est qu'il ne fonctionne pas comme un détecteur NaN, c'est juste très intéressant ici):Résultats avec Visual C ++:
Pour résumer les résultats ci-dessus, seuls les tests directs de la représentation au niveau du bit, en utilisant la
is_ieee754_nan
fonction définie dans ce programme de test, ont fonctionné de manière fiable dans tous les cas avec g ++ et Visual C ++.Addendum:
Après avoir publié ce qui précède, j'ai pris connaissance d'un autre test possible pour NaN, mentionné dans une autre réponse ici, à savoir
((value < 0) == (value >= 0))
. Cela s'est avéré fonctionner correctement avec Visual C ++ mais a échoué avec l'-ffast-math
option de g ++ . Seuls les tests Bitpattern directs fonctionnent de manière fiable.la source
Cela fonctionne si
sizeof(int)
est 4 etsizeof(long long)
est 8.Pendant l'exécution, ce n'est qu'une comparaison, les castings ne prennent pas de temps. Il modifie simplement la configuration des drapeaux de comparaison pour vérifier l'égalité.
la source
memcpy
, à travers un tableau d'octets pour être sûr. Code pour cela dans ma réponse n ° 2 .Une solution possible qui ne dépendrait pas de la représentation IEEE spécifique pour NaN utilisée serait la suivante:
la source
Étant donné que (x! = X) n'est pas toujours garanti pour NaN (comme si vous utilisez l'option -ffast-math), j'ai utilisé:
Les nombres ne peuvent pas être à la fois <0 et> = 0, donc cette vérification n'est réussie que si le nombre n'est ni inférieur, ni supérieur ou égal à zéro. Ce qui est fondamentalement pas de nombre du tout, ou NaN.
Vous pouvez également l'utiliser si vous préférez:
Je ne sais pas comment cela est affecté par -ffast-math, donc votre kilométrage peut varier.
la source
f != f
est aussi. J'ai vu llvm optimiser un morceau de code presque identique. L'optimiseur peut propager les informations sur la première comparaison et comprendre que la deuxième comparaison peut ne jamais être vraie si la première l'est. (si le compilateur obéit strictement aux règles IEEEf != f
est de toute façon beaucoup plus simple)-ffast-math
option de g ++ . Fonctionne avec Visual C ++. Voir ( stackoverflow.com/a/42138465/464581 ).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. Elle repose sur le fait que le seul cas où une valeur n'est pas égale à elle-même est lorsque la valeur n'est pas un nombre.
la source
Cela marche:
sortie: isnan
la source
Il me semble que la meilleure approche véritablement multiplateforme serait d'utiliser une union et de tester la configuration binaire du double pour vérifier les NaN.
Je n'ai pas testé cette solution en profondeur, et il peut y avoir une façon plus efficace de travailler avec les modèles de bits, mais je pense que cela devrait fonctionner.
la source
union
type de jeu de mots entre deux types peut ne pas fonctionner comme souhaité (: sad_panda :). La bonne façon (bien que pas aussi portable que souhaité) serait d'éviter complètement l'union et de faire un memcpy dedouble
dans uneuint64_t
variable différente , puis de faire le test en utilisant cette variable auxiliaire.Sur x86-64, vous pouvez avoir des méthodes extrêmement rapides pour vérifier NaN et infini, qui fonctionnent indépendamment de l'
-ffast-math
option du compilateur. (f != f
,std::isnan
,std::isinf
Cédez toujoursfalse
avec-ffast-math
).Les tests pour NaN, l'infini et les nombres finis peuvent facilement être effectués en vérifiant l'exposant maximum. l'infini est l'exposant maximum avec une mantisse nulle, NaN est l'exposant maximum et une mantisse non nulle. L'exposant est stocké dans les bits suivants après le bit de signe le plus haut, afin que nous puissions simplement déplacer à gauche pour se débarrasser du bit de signe et faire de l'exposant les bits du haut, aucun masquage (
operator&
) n'est nécessaire:Les
std
versions deisinf
etisfinite
chargent 2double/float
constantes à partir du.data
segment et dans le pire des cas, elles peuvent provoquer 2 échecs de cache de données. Les versions ci-dessus ne chargent aucune donnéeinf_double_shl1
et lesinf_float_shl1
constantes sont encodées en tant qu'opérandes immédiats dans les instructions d'assemblage.Plus rapide
isnan2
est juste 2 instructions de montage:Utilise le fait que l'
ucomisd
instruction définit l'indicateur de parité si un argument est NaN. C'est ainsi questd::isnan
fonctionne lorsqu'aucune-ffast-math
option n'est spécifiée.la source
La norme IEEE dit que lorsque l'exposant est tout
1
s et que la mantisse n'est pas nulle, le nombre est aNaN
. Le double est le1
bit de signe,11
les bits d'exposant et les52
bits de mantisse. Vérifiez un peu.la source
Comme les commentaires ci-dessus indiquent a! = A ne fonctionnera pas dans g ++ et certains autres compilateurs, mais cette astuce devrait. Ce n'est peut-être pas aussi efficace, mais c'est quand même un moyen:
Fondamentalement, dans g ++ (je ne suis pas sûr pour les autres cependant) printf affiche 'nan' aux formats% d ou% .f 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»)
la source
340282346638528859811704183484516925440.000
si a =FLT_MAX
. Il devrait utiliserchar s[7]; sprintf(s, "%.0g", a);
, qui sera de 6 heures sia=-FLT_MAX
, ou-3e+38
Cela détecte l'infini et également NaN dans Visual Studio en vérifiant qu'il est dans les doubles limites:
la source
FLT_MIN
,DBL_MIN
etLDBL_MIN
plus attentivement. Ces valeurs sont définies comme étant les plus petites valeurs normalisées pour chaque type. Par exemple, la simple précision a plus de 8 millions de valeurs de denorm légitimes qui sont supérieures à zéro et inférieures àFLT_MIN
(et ne sont pas NaN).