NaN (Not a Number) est une valeur spéciale propre aux types à virgule flottante (float, double, long double). Elle ne représente pas un nombre au sens mathématique, mais un état indiquant une opération arithmétique invalide ou un résultat indéfini.
Les situations courantes qui génèrent un NaN incluent :
- Opérations mathématiques invalides :
0.0 / 0.0(division de zéro par zéro),sqrt(-1.0)(racine carrée d'un nombre négatif). - Conversions absurdes : tenter de convertir une chaîne non numérique comme
"abc"viastd::stod(cela lève une exception, mais la représentation flottante sous-jacente peut correspondre à un NaN). - Autres calculs spéciaux :
inf - inf(soustraction de deux infinis),0.0 * inf(multiplication d'un zéro par un infini).
- La méthode standard : std::isnan
En C++, la fonction std::isnan est l'outil officiel pour tester si une valeur flottante est un NaN. Elle est disponible via l'en-tête <cmath> (recommandé) ou <math.h> (compatibilité C).
Prototype de la fonction :
#include <cmath>
bool isnan(float arg);
bool isnan(double arg);
bool isnan(long double arg);
Retour :
true(valeur booléenne explicite depuis C++11) si l'argument est NaN.falsedans le cas contraire (incluant les valeurs normales, zéro, infini positif ou négatif).
- Propriété clé : l'échec de la réflexivtié
Le NaN possède une caractéristique unique : il n'est égal à rien, pas même à lui-même. Autrement dit, pour tout NaN x, l'expression x == x est toujours fausse.
C'est pourquoi une comparaison directe comme if (x == NaN) ne fonctionne pas. L'utilisation de std::isnan est impérative.
#include <iostream>
#include <cmath>
#include <limits>
int main() {
double valeurNaN = std::numeric_limits<double>::quiet_NaN();
// Méthode erronée : ne détecte pas NaN
if (valeurNaN == valeurNaN) {
std::cout << "Ce message ne s'affichera jamais." << std::endl;
}
// Méthode correcte
if (std::isnan(valeurNaN)) {
std::cout << "La valeur est NaN." << std::endl;
}
return 0;
}
- Les deux types de NaN
4.1 NaN silecnieux (Quiet NaN, QNaN)
Caractéristique : Il ne déclenche aucune exception matérielle. Le programme continue son exécution normalement. C'est le type de NaN le plus fréquent.
Obtention : std::numeric_limits<T>::quiet_NaN() (nécessite <limits>).
Usage : Représenter des valeurs invalides dans des calculs standards (résultat d'opération non définie, variable non initialisée).
4.2 NaN signalant (Signaling NaN, SNaN)
Caractéristique : Sa création ou son utilisation dans une opération flottante déclenche une exception (signal). Cela peut interrompre le programme ou activer un gestionnaire d'erreur.
Obtention : std::numeric_limits<T>::signaling_NaN().
Usage : Principalement destiné au débogage pour détecter les opérations flottantes invalides. Rarement utilisé en production.
Exemple complet :
#include <iostream>
#include <cmath>
#include <limits>
int main() {
// 1. Générer des NaN
double zero = 0.0;
double nanCalc = zero / zero; // NaN par calcul
double nanSilencieux = std::numeric_limits<double>::quiet_NaN();
double nanSignalant = std::numeric_limits<double>::signaling_NaN();
// 2. Détection avec std::isnan
std::cout << "nanCalc est NaN : " << (std::isnan(nanCalc) ? "oui" : "non") << std::endl;
std::cout << "nanSilencieux est NaN : " << (std::isnan(nanSilencieux) ? "oui" : "non") << std::endl;
std::cout << "nanSignalant est NaN : " << (std::isnan(nanSignalant) ? "oui" : "non") << std::endl;
// 3. Vérification de la non-réflexivité
double normal = 3.14159;
std::cout << "Comparaison normale : " << (normal == normal ? "true" : "false") << std::endl;
std::cout << "Comparaison NaN : " << (nanCalc == nanCalc ? "true" : "false") << std::endl;
// 4. L'infini n'est pas NaN
double infini = 1.0 / zero;
std::cout << "Infini est NaN : " << (std::isnan(infini) ? "oui" : "non") << std::endl;
return 0;
}
Sortie attendue :
nanCalc est NaN : oui
nanSilencieux est NaN : oui
nanSignalant est NaN : oui
Comparaison normale : true
Comparaison NaN : false
Infini est NaN : non