Ceci est une ancienne révision du document !
ppliquent qu'aux nombres réels. Si vous écriv
Vous avez vu dans les chapitres précédents comment écrire et manipuler les nombres entiers. Et vous avez vu que le C++ faisait la distinction entre les nombres entiers (nombres “sans virgules”) et les nombres réels (nombre “avec virgule”). En particulier, lors d'une division, le résultat est totalement différent selon le type de nombre.
#include <iostream> int main() { std::cout << "11 / 4 = " << 11 / 4 << std::endl; // division entière std::cout << "11.0 / 4.0 = " << 11.0 / 4.0 << std::endl; // division réelle }
affiche :
11 / 4 = 2 11.0 / 4.0 = 2.75
Pour faire des calculs mathématiques, les nombres entiers ne seront généralement pas suffisant. Par exemple, si vous souhaitez calculer la circonférence d'un cercle à partir de son diamètre, il faudra multiplier celui-ci par le nombre Pi, qui vaut environ 3,14159265358979323846264338327950288… Ce types de nombres et de calculs ne peuvent pas être réalisé avec des nombres entiers, il faudra donc utiliser des nombres conçus spécialement dans ce but : les nombres à virgule flottante (floating-point numbers), que l'on appelle aussi nombres réels.
Les nombres à virgule flottante sont donc simplement des nombres qui s'écrivent avec des chiffres après la virgule, comme par exemple Pi.
Un rappel important : en français, les nombres réels sont écrit avec une virgule (comma en anglais). Le C++ est basé sur l'anglais, il utilise donc le point comme séparateur décimal. La virgule ayant un sens spécifique en C++, vous n'aurez peut être pas de message si vous faites l'erreur. Faites donc attention sur ce point.
À partir de là, l'écriture de nombres réels est relativement simple, par exemple pour écrire quelques constantes de physique :
#include <iostream> int main() { std::cout << "Pi = " << 3.1415926535 << std::endl; std::cout << "Nombre d'or = " << 1.6180339887 << std::endl; std::cout << "Vitesse de la lumière = " << 299792.458 << " km/s" << std::endl; }
affiche :
Pi = 3.14159 Nombre d'or = 1.61803 Vitesse de la lumière = 299792 km/s
Comme pour les nombres entiers, il est possible d'utiliser le guillemet droit simple ' pour faciliter la lecture des nombres contenant beaucoup de chiffres. Par convention, on écrit séparé généralement en bloc de 3 chiffres :
#include <iostream> int main() { std::cout << "Pi = " << 3.141'592'653'5 << std::endl; std::cout << "Nombre d'or = " << 1.618'033'988'7 << std::endl; std::cout << "Vitesse de la lumière = " << 299'792.458 << " km/s" << std::endl; }
Lorsque la partie entière (la partie à gauche du point) ou la partie décimale (à droite du point) d'un nombre ne contient que des zéros, il est possible de ne pas les écrire. Par exemple, “0.123” est équivalent à ”.123” et “132.0” est équivalent à “123.”. Mais faites attention de ne pas oublier le point, sinon cela correspondra à un nombre entier :
#include <iostream> int main() { std::cout << 0.123 << " = " << .123 << std::endl; std::cout << 123.0 << " = " << 123. << std::endl; std::cout << (123. / 5.) << " != " << (123 / 5) << std::endl; }
ce qui affiche :
0.123 = 0.123 123 = 123 24.6 != 24
Pour Pi ou la vitesse de la lumière c, cela ne pose pas de problème de les écrire comme présenté au-dessus. Par contre, si l'on souhaite écrire par exemple la constante de perméabilité magnétique du vide µ0, il faudra écrire :
std::cout << "Perméabilité magnétique du vide = " << 0.000'001'256'637'061'4 << " kg.m/A²/s²" << std::endl;
Ce qui commence à faire beaucoup de zéro. Si on veut écrire la constante de Planck, c'est encore plus compliqué : il faut écrire 34 zéro après le séparateur décimal. On ne va pas le faire, cela devient trop compliqué. Heureusement, le C++ prend en charge la notation scientifique des nombres réels.
La notation scientifique consiste à écrire un nombre sous la forme :
$$ mantisse \times 10 ^ { exposant } $$
La mantisse (mantissa ou significand en anglais) est un nombre réel positif ou négatif et l'exposant (exponent en anglais) est un nombre entier positif ou négatif.
Pour obtenir la notation scientifique d'un nombre, il faut multiplier ou division ce nombre plusieurs fois par 10, jusqu'à obtenir un nombre supérieur ou à égal à 1 et strictement inférieur à 10. Le résultat est la mantisse, le nombre de fois que l'on a multiplié ou divisé par 10 est l'exposant.
Par exemple, si on prend le nombre 0.000123. On peut écrire :
0.000123 * 10 = 0.00123 0.00123 * 10 = 0.0123 0.0123 * 10 = 0.123 0.123 * 10 = 1.23
Il faut donc multiplier 4 fois 0.000123 par 10 pour obtenir 1.23. Le nombre 0.000123 peut donc s'écrire :
$$ 1.23 \times 10 ^ { 4 } $$
Pour écrire un nombre en C++ en utilisant cette notation, il faut ajouter l'exposant après le caractère “e” ou “E” dans l'écriture d'un nombre. Par exemple :
#include <iostream> int main() { std::cout << "Perméabilité magnétique du vide = " << 1.256'637'061'4e-6 << " kg.m/A²/s²" << std::endl; std::cout << "Constante de Planck = " << 6.626'069'57e-34 << " kg.m²/s" << std::endl; }
affiche :
Perméabilité magnétique du vide = 1.25664e-06 kg.m/A²/s² Constante de Planck = 6.62607e-34 kg.m²/s
Un dernier point sur la notation scientifique. Si vous écrivez :
#include <iostream> int main() { std::cout << 123456789.0e10 << std::endl; std::cout << 0.0000000123456789e-10 << std::endl; }
cela affichera :
1.23457e+18 1.23457e-18
On peut remarquer deux points sur le résultat affiché :
Il est possible de changer l'affichage des nombres entiers avec std::cout
en utilisant des directives. Il existe également des directives pour modifier l'affichage des nombres réels.
La première chose que vous allez pouvoir modifier est l'utilisation de la notation scientifique ou non. La directive std::scientific
force l'affichage en notation scientifique, std::fixed
force l'affichage sans notation scientifique et std::defaultfloat
affiche en utilisant la notation par défaut.
#include <iostream> int main() { std::cout << "Pi" << std::endl; std::cout << " Notation scientifique : " << std::scientific << 3.141'592'653'5 << std::endl; std::cout << " Notation fixe : " << std::fixed << 3.141'592'653'5 << std::endl; std::cout << " Notation par défaut : " << std::defaultfloat << 3.141'592'653'5 << std::endl; std::cout << std::endl; std::cout << "Constante de Planck" << std::endl; std::cout << " Notation scientifique : " << std::scientific << 6.626'069'57e-34 << std::endl; std::cout << " Notation fixe : " << std::fixed << 6.626'069'57e-34 << std::endl; std::cout << " Notation par défaut : " << std::defaultfloat << 6.626'069'57e-34 << std::endl; }
affiche :
Pi Notation scientifique : 3.141593e+00 Notation fixe : 3.141593 Notation par défaut : 3.14159 Constante de Planck Notation scientifique : 6.626070e-34 Notation fixe : 0.000000 Notation par défaut : 6.62607e-34
Notez bien que ces directives ne s'appliquent qu'aux nombres réels. Si vous écrivez un nombre entier, celui ne sera pas affiché en notation scientifique :
#include <iostream> int main() { std::cout << std::scientific << 3.0 << std::endl; std::cout << std::scientific << 3 << std::endl; }
affiche :
3.000000e+00 3
Même si 3
, 3.0
, “3”
et '3' représente la même chose pour vous (le chiffre 3), ces valeurs ont des types différents (respectivement un nombre entier, un nombre réel, une chaîne de caractères et un caractère) et s'utilisent différemment en C++.
Pour afficher le signe positif devant les nombres positif, vous pouvez utiliser la directive std::showpos
et la directive std::noshowpos
pour ne pas les afficher. La directive std::showpoint
permet d'afficher les chiffres après le séparateur décimal et std::noshowpoint
pour ne pas les afficher.
#include <iostream> int main() { std::cout << std::showpos << 123.456 << std::endl; std::cout << std::showpos << 0.123 << std::endl; std::cout << std::showpos << 123.0 << std::endl; std::cout << std::noshowpos << 123.456 << std::endl; std::cout << std::endl; std::cout << std::showpoint << 123.0 << std::endl; std::cout << std::noshowpoint << 123.0 << std::endl; }
affiche :
+123.456 +0.123 +123 123.456 123.000 123
Pour terminer, la directive std::setprecision
permet de définir le nombre de chiffre significatif à afficher (donc sans compter les zéro au début et à la fin des nombres réels). Cette directive prend un nombre entier en paramètre, correspondant aux nombres de chiffres à afficher.
Cette directive est disponible dans le fichier d'en-tête iomanip
, il faut donc l'inclure au début du programme avec la directive de préprocesseur #include
:
#include <iostream> #include <iomanip> int main() { std::cout << "Pi (défaut) = " << 3.141'592'653'5 << std::endl; std::cout << "Pi (précision 0) = " << std::setprecision(0) << 3.141'592'653'5 << std::endl; std::cout << "Pi (précision 1) = " << std::setprecision(1) << 3.141'592'653'5 << std::endl; std::cout << "Pi (précision 2) = " << std::setprecision(2) << 3.141'592'653'5 << std::endl; std::cout << "Pi (précision 5) = " << std::setprecision(5) << 3.141'592'653'5 << std::endl; std::cout << "Pi (précision 10) = " << std::setprecision(10) << 3.141'592'653'5 << std::endl; std::cout << "Pi (précision 30) = " << std::setprecision(15) << 3.141'592'653'5 << std::endl; std::cout << "Pi (précision 200) = " << std::setprecision(200) << 3.141'592'653'5 << std::endl; std::cout << "Pi (valeur invalide) = " << std::setprecision(-1) << 3.141'592'653'5 << std::endl; }
affiche :
Pi (défaut) = 3.14159 Pi (précision 0) = 3 Pi (précision 1) = 3 Pi (précision 2) = 3.1 Pi (précision 5) = 3.1416 Pi (précision 10) = 3.141592654 Pi (précision 30) = 3.14159265350000005412312020781 Pi (précision 200) = 3.1415926535000000541231202078051865100860595703125 Pi (valeur invalide) = 3.14159
Vous pouvez remarquer que si vous définissez une précision supérieure au nombre de chiffre que vous avez écrit dans votre littérale (par exemple 30 dans le code précédent), alors std::cout
affichera des chiffres incorrectes à la fin de votre nombre.
Si vous définissez une valeur très grande (200 dans le code précédent), alors le nombre de chiffres affichés sera limité à 50 maximum (le nombre maximal de chiffres peut varier selon le compilateur et le système).
Pour terminer, si vous définissez une valeur négative, alors std::cout
affiche de nouveau les nombres en utilisant la précision par défaut (6 chiffres dans l'exemple précédent).
Comme pour les nombres entiers, il est possible de réaliser des calculs arithmétiques avec les nombres réels : addition +
, soustraction -
, multiplication *
et division /
(n'oubliez pas la différence entre division entière et réelle). L'opérateur modulo n'a pas de sens pour les nombres réels.
#include <iostream> int main() { std::cout << "Addition : " << 12.34 + 56.78 << std::endl; std::cout << "Soustraction : " << 12.34 - 56.78 << std::endl; std::cout << "Multiplication: " << 12.34 * 56.78 << std::endl; std::cout << "Division : " << 12.34 / 56.78 << std::endl; }
affiche :
Addition : 69.12 Soustraction : -44.44 Multiplication: 700.665 Division : 0.21733
De la même manière, vous pouvez utiliser les opérateurs de comparaison présentés dans le chapitre sur les entiers : est égale ==
, est différent !=
, est supérieur >
, est supérieur ou égal >=
, est inférieur <
, est inférieur ou égal ⇐
.
#include <iostream> int main() { std::cout << std::boolalpha; std::cout << "Est égale : " << (12.34 == 56.78) << std::endl; std::cout << "Est différent : " << (12.34 != 56.78) << std::endl; std::cout << "Est supérieur : " << (12.34 > 56.78) << std::endl; std::cout << "Est supérieur ou égal : " << (12.34 >= 56.78) << std::endl; std::cout << "Est inférieur : " << (12.34 < 56.78) << std::endl; std::cout << "Est inférieur ou égal : " << (12.34 <= 56.78) << std::endl; }
affiche :
Est égale : false Est différent : true Est supérieur : false Est supérieur ou égal : false Est inférieur : true Est inférieur ou égal : true
Encore une fois, il faut insister sur un point très important : ce qui est affiché ne correspond pas forcement à ce qu'il y a en mémoire. Par exemple :
#include <iostream> int main() { std::cout << 3.0000000000001 << std::endl; std::cout << 3.0000000000000001 << std::endl; std::cout << std::boolalpha << (3 == 3.0000000000001) << std::endl; std::cout << std::boolalpha << (3 == 3.0000000000000001) << std::endl; }
affiche :
3 3
Si vous utiliser l'opérateur d'égalité sur ces nombres, vous aurez peut être des surprises :
#include <iostream> int main() { std::cout << 3 << " est égal à " << 3.0000000000001 << " ? " << std::boolalpha << (3 == 3.0000000000001) << std::endl; std::cout << 3 << " est égal à " << 3.0000000000000001 << " ? " << std::boolalpha << (3 == 3.0000000000000001) << std::endl; }
affiche :
3 est égal à 3 ? false 3 est égal à 3 ? true
Les nombres 3, 3.0000000000001 et 3.0000000000000001 sont affichés de la même façon (ce qui est normal, si vous vous souvenez du rôle de std::setprecision
). Par contre, le résultat du test d'égalité change aussi, ce qui est plus surprenant.
Dans le premier cas, 3.0000000000001 est effectivement enregistré en mémoire comme différent de 3. Le test d'égalité échoue, puisque l'ordinateur sait que ces nombres sont différents (même cela affiche le même nombre). Dans le second cas, le nombre dépasse les capacités de l'ordinateur (plus précisément, de la façon dont les nombres sont enregistré en mémoire). Le nombre est effectivement arrondi en mémoire à 3 et le test d'égalité n'échoue pas.
Il est facile de voir ici que les nombres entrés sont différents. Mais imaginez que vous réalisez des calculs scientifiques complexes. Cela pourrait générer des nombres qui semblent identiques (à l'affichage), mais qui sont en fait différents.
Pour éviter cela, en général, on ne compare pas directement l'égalité de deux nombres réels (sauf cas particulier). On va considérer que deux nombres sont égaux s'ils sont suffisamment proche, compte tenu d'une éventuelle erreur de précision. Ou dis autrement, que la valeur absolue de la différence entre deux nombres est inférieure à epsilon (nous reviendrons sur la valeur absolue plus tard).
std::abs(nombre1 - nombre2) < epsilon
La valeur maximale de l'erreur de précision est appelée epsilon. Elle va dépendre de type de calculs que vous réalisez, de la précision des nombres, de l'ordinateur, etc. Bref, cela va dépendre du contexte et il est difficile de donner une valeur fixée pour epsilon dans ce cours.
#include <iostream> #include <cmath> int main() { std::cout << 3 << " est égal à " << 3.0000000000001 << " ? " << std::boolalpha << (std::abs(3 - 3.0000000000001) < 0.0001) << std::endl; std::cout << 3 << " est égal à " << 3.0000000000000001 << " ? " << std::boolalpha << (std::abs(3 - 3.0000000000000001) < 0.0001) << std::endl; }
affiche :
3 est égal à 3 ? true 3 est égal à 3 ? true
Vous avez vu dans le chapitre sur les entiers que la division par zéro produisait un comportement indéterminé (undefined behavior). Essayons le même code en utilisant des nombres réels :
#include <iostream> int main() { std::cout << "1.0 / 0.0 = " << (1.0 / 0.0) << std::endl; }
affiche :
1.0 / 0.0 = inf
Contrairement à la division sur les nombres entiers, la division sur les nombres réels ne produit pas un comportement indéterminé. Le résultat donné “inf” signifie “infini”, ce qui a un sens au niveau mathématique.
Si on modifie le code pour calculer la division de 0 par 0 :
#include <iostream> int main() { std::cout << "0.0 / 0.0 = " << (0.0 / 0.0) << std::endl; }
on obtient :
0.0 / 0.0 = nan
Le résultat affiche est alors “nan”, ce qui signifie “Not a Number”, en français “pas un nombre”. Ce résultat est obtenu lorsqu'un calcul n'a pas de sens en termes de mathématique. Mais du point de vue du C++, cela n'est pas non plus une erreur (au sens d'erreur de calcul ou d'exécution).
Ceux deux valeurs particulières “infini” et “not a number” sont parfaitement définie en C++. Pour écrire directement ces valeurs, vous pouvez utiliser les macros INFINITY
et NAN
définie dans le fichier d'en-tête cmath
:
#include <iostream> #include <cmath> int main() { std::cout << "Infini : " << INFINITY << std::endl; std::cout << "Not a number : " << NAN << std::endl; }
affiche :
Infini : inf Not a number : nan
Cependant, dans la majorité des cas, vous aurez plus souvent besoin de vérifier qu'un calcul ne produit pas une de ces valeurs. Pour cela, vous pouvez utiliser une des fonctions suivantes :
std::isinf()
pour tester si une valeur est infinie ;std::isnan()
pour tester si une valeur est “not a number” ;std::isfinite()
pour tester si une valeur est finie, c'est à dire qu'elle n'est pas “not a number” ou infinie.
Ces fonctions sont également définie dans le fichier d'en-tête cmath
, il faut donc l'inclure dans votre code.
#include <iostream> #include <cmath> int main() { std::cout << std::boolalpha; std::cout << "isinf(1.0 / 1.0) = " << std::isinf(1.0 / 1.0) << std::endl; std::cout << "isinf(0.0 / 1.0) = " << std::isinf(0.0 / 1.0) << std::endl; std::cout << "isinf(1.0 / 0.0) = " << std::isinf(1.0 / 0.0) << std::endl; std::cout << "isinf(0.0 / 0.0) = " << std::isinf(0.0 / 0.0) << std::endl; std::cout << std::endl; std::cout << "isnan(1.0 / 1.0) = " << std::isnan(1.0 / 1.0) << std::endl; std::cout << "isnan(0.0 / 1.0) = " << std::isnan(0.0 / 1.0) << std::endl; std::cout << "isnan(1.0 / 0.0) = " << std::isnan(1.0 / 0.0) << std::endl; std::cout << "isnan(0.0 / 0.0) = " << std::isnan(0.0 / 0.0) << std::endl; std::cout << std::endl; std::cout << "isfinite(1.0 / 1.0) = " << std::isfinite(1.0 / 1.0) << std::endl; std::cout << "isfinite(0.0 / 1.0) = " << std::isfinite(0.0 / 1.0) << std::endl; std::cout << "isfinite(1.0 / 0.0) = " << std::isfinite(1.0 / 0.0) << std::endl; std::cout << "isfinite(0.0 / 0.0) = " << std::isfinite(0.0 / 0.0) << std::endl; std::cout << std::noboolalpha << std::endl; std::cout << "fpclassify(1.0 / 1.0) = " << std::fpclassify(1.0 / 1.0) << std::endl; std::cout << "fpclassify(0.0 / 1.0) = " << std::fpclassify(0.0 / 1.0) << std::endl; std::cout << "fpclassify(1.0 / 0.0) = " << std::fpclassify(1.0 / 0.0) << std::endl; std::cout << "fpclassify(0.0 / 0.0) = " << std::fpclassify(0.0 / 0.0) << std::endl; }
affiche :
isinf(1.0 / 1.0) = false isinf(0.0 / 1.0) = false isinf(1.0 / 0.0) = true isinf(0.0 / 0.0) = false isnan(1.0 / 1.0) = false isnan(0.0 / 1.0) = false isnan(1.0 / 0.0) = false isnan(0.0 / 0.0) = true isfinite(1.0 / 1.0) = true isfinite(0.0 / 1.0) = true isfinite(1.0 / 0.0) = false isfinite(0.0 / 0.0) = false fpclassify(1.0 / 1.0) = 4 fpclassify(0.0 / 1.0) = 2 fpclassify(1.0 / 0.0) = 1 fpclassify(0.0 / 0.0) = 0
Pour terminer avec l'utilisation de base des nombres réels, le C++ propose de nombreuses fonctions mathématiques usuelles, comme le calcul des puissances et des racines ou les fonctions trigonométriques. Nous n'allons pas toutes les voir dans ce chapitre, elles ne présentent pas de difficultés particulières d'utilisation. Ces fonctions sont définies dans le fichier d'en-tête cmath
, vous trouverez la totalité des fonctions dans la documentation : Common mathematical functions.
Voici quelques exemples d'utilisation :
#include <iostream> #include <cmath> int main() { std::cout << "Fonctions divers" << std::endl; std::cout << "valeur absolue : " << std::fabs(-1.0) << std::endl; std::cout << "min : " << std::fmin(1.0, 2.0) << std::endl; std::cout << "max : " << std::fmax(1.0, 2.0) << std::endl; std::cout << "x*y+z (Fused Multiply-Add) : " << std::fma(2.0, 3.0, 4.0) << std::endl; std::cout <<std::endl; std::cout << "Fonctions trigonométries" << std::endl; std::cout << "cosinus : " << cos(1.0) << std::endl; std::cout << "sinus : " << sin(-1.0) << std::endl; }
affiche :
Fonctions divers valeur absolue : 1 min : 1 max : 2 x*y+z (Fused Multiply-Add) : 10 Fonctions trigonométries cosinus : 0.540302 sinus : -0.841471