==
entre des flottants ?NaN
?double
aux float
?Sur la plupart des architectures, les nombres réels (dits flottants) sont représentés en base 2, comme pour les entiers. Ainsi, le nombre 3.1 ne peut s'écrire exactement en base 2. La représentation binaire est un arrondi qui dépend de la précision du codage des flottants, et des choix du compilateur. De plus, avec une fonction comme printf(), le nombre à imprimer est converti en base 2 puis reconverti en base 10, ce qui augmente encore les imprécisions.
Il est préférable d'utiliser les double
, qui ont une
précision supérieure aux float
, sauf si l'économie de
mémoire est vraiment critique.
Voir à ce sujet la question 11.10.
Assurez-vous d'avoir inclus math.h
, d'avoir correctement
déclaré les autres fonctions renvoyant des double
. Une
autre fonction de la bibliothèque standard avec laquelle il faut
faire attention est atof(), dans stdlib.h
.
Il faut s'assurer d'avoir linké (lié) son code avec
la bibliothèque mathématique. Par exemple, sous Unix,
vous devez généralement passer l'option -lm
à la fin de la
ligne de commande.
Pour commencer, relisez 11.1. Si le problème est plus complexe, il convient de se rappeler que les ordinateurs utilisent des formats de représentation des flottants qui ne permettent pas des calculs exacts. Pertes de précision, accumulation d'erreurs et autres anomalies sont le lot commun du numéricien.
Rappelez-vous qu'aucun calcul sur des flottants n'a de chance
d'être exact, en particulier, n'utilisez jamais ==
entre
deux flottants.
Ces problèmes ne sont pas spécifiques au C.
Dans certains problèmes, une solution peut être d'introduire un
petit paramètre de relaxation, par exemple #define EPS
1e-10
, puis de multiplier l'un des termes (judicieusement
choisi) de vos calculs par (1 + EPS)
.
Pour plus de renseignements, on se reportera par exemple aux Numerical Recipes ou à Numerical Algorithms with C (cf. 3.9).
==
entre des flottants ?Étant donné qu'il y a perte de précision très vite, pour comparer deux valeurs flottantes, on teste si elles sont assez proches. Plutôt que d'écrire une horreur du genre :
double a, b; /* ... */ if (a == b) /* HORREUR ! */ /* ... */
on écrira :
#include <math.h> /* ... */ double a, b; /* ... */ if (fabs(a - b) <= epsilon * fabs(a)) /* ... */
où l'on aura judicieusement choisi epsilon
(non-nul !).
La méthode la plus simple et la plus expéditive est (int)(x +
0.5)
. Cette technique ne fonctionne pas correctement pour
les nombres négatifs aussi vaut-il mieux utiliser
(int)(x < 0 ? x - 0.5 : x + 0.5)
Parce que certains processeurs ne disposent pas d'une telle
instruction. Il existe une fonction pow() déclarée dans
math.h
bien que la multiplication soit préférable pour de
petits exposants.
Parfois une constante prédéfinie M_PI
est déclarée dans
math.h
mais ce n'est pas standard aussi vaut-il mieux
calculer π soi-même via 4 * atan
(1.0)
.
NaN
?« NaN is Not a Number »
, ce qui signifie
« Ce n'est pas un nombre ».
Un NaN
est un nombre flottant qui est le résultat d'une
opération non conforme, par exemple 0/0
.
Lorsqu'un NaN
est produit, la plupart des architectures
produisent une interruption (ou un signal) qui termine le
programme, au moment de l'utilisation de celui-ci.
Il est parfois possible de vérifier si un nombre est
NaN
. Un bon test est celui-ci :
#define isNaN(x) ((x) != (x))
Certains compilateurs fournissent des facilités quant à la gestion
des NaN
. GCC fournit dans la bibliothèque
mathématique (math.h
) les fonctions isnan(),
isinf() et
finite().
double
aux float
?La vitesse de traitement d'un double
n'est pas forcément plus
longue qu'un float
, cela dépend du compilateur (de ses options)
et du processeur. Ainsi avec l'exemple suivant, en remplaçant le
typedef
par float
ou double
, on s'aperçoit
que sur un Pentium ou un PowerPC, le double
est plus rapide
à calculer que le float
tout en ayant une précision plus grande.
#include <stdio.h> #include <math.h> typedef float reel; /* float ou double */ int main(void) { long i; reel d = 3.0; for (i = 0; i < 100000000; i++) { d = cos(d); } (void)printf("%f\n", d); return 0; }
Le C comprend des instructions mathématiques pour traiter les float
directement au lieu de toujours passer par des double depuis la
dernière norme (C99). Par exemple il existe cosf() en plus
de cos(). En faisant des essais on s'aperçoit que dans
notre exemple, le cosf() appliqué à un float
devient
aussi rapide que le cos() appliqué à un double
.
En conclusion, nous pouvons dire qu'il est préférable d'utiliser des
double
à la place des float
, sauf lorsque la place
mémoire devient critique.