Le cœur du langage C vous donne + - * / et %, et rien d'autre - pas d'opérateur puissance, pas de racine carrée, pas d'arrondi. Tout ce qui dépasse l'arithmétique vit dans la bibliothèque mathématique standard, déclarée dans <math.h>.
Ces fonctions travaillent toutes en précision double : elles prennent des arguments double et renvoient des double. C'est la première chose à intégrer, car passer un int fonctionne silencieusement (il est converti) alors qu'affecter le résultat à un int jette silencieusement la fraction.
Pour commencer, et l'option -lm
Sur votre propre machine, compiler cela peut échouer :
/usr/bin/ld: /tmp/ccXYZ.o: in function `main':
program.c:(.text+0x1a): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status
C'est le faux pas le plus courant avec math.h, et ce n'est pas une erreur dans votre code. L'en-tête a fourni les déclarations, donc le compilateur était satisfait ; les implémentations vivent dans une bibliothèque séparée (libm) que l'éditeur de liens n'inclut pas par défaut sous Linux. Ajoutez -lm :
gcc program.c -o program -lm
L'option doit venir après les fichiers sources - les éditeurs de liens traitent leurs entrées dans l'ordre, et une bibliothèque listée avant le code qui en a besoin ne résout rien. Sous macOS et avec MinGW sous Windows, les routines mathématiques font déjà partie de la bibliothèque C standard, donc -lm est inutile (quoique sans danger sous macOS).
Puissances et racines
sqrt(x) d'un nombre négatif renvoie NaN au lieu de planter ; protégez l'argument s'il peut être négatif. hypot mérite d'être connue car elle calcule la même valeur que sqrt(x*x + y*y) sans déborder quand x ou y est très grand.
Pour de petites puissances entières, la simple multiplication est à la fois plus rapide et exacte - x * x bat pow(x, 2.0), qui passe par des logarithmes et peut renvoyer 8,999999999999998 là où vous attendiez 9.
L'arrondi : floor, ceil, round, trunc
Quatre fonctions, quatre réponses différentes pour les nombres négatifs. Ce tableau est la partie à mémoriser :
floor(x)- le plus grand entier non supérieur àx. Va toujours vers moins l'infini :floor(-2.3)vaut-3.ceil(x)- le plus petit entier non inférieur àx. Va toujours vers plus l'infini :ceil(-2.3)vaut-2.round(x)- l'entier le plus proche, les demis s'éloignant de zéro :round(2.5)vaut3,round(-2.5)vaut-3.trunc(x)- coupe la partie fractionnaire, en allant vers zéro :trunc(-2.7)vaut-2.
Les quatre renvoient un double. Convertir en int quand vous voulez un entier convient, mais notez que (int)x tout seul effectue une troncature - donc (int)(x + 0.5) est le vieil idiome d'arrondi, et il est faux pour les nombres négatifs. Utilisez round().
fabs et fmod
fabs est la valeur absolue en virgule flottante. Prendre abs à la place est une erreur classique, car abs prend un int et la conversion se fait silencieusement :
% ne fonctionne pas du tout sur un double - c'est un opérateur entier et le compilateur le rejette. fmod(a, b) en est l'équivalent flottant, et elle conserve le signe de a.
fabs est aussi la bonne façon de comparer deux double, puisque == sur du flottant est un piège :
0.1 et 0.2 ne peuvent pas être représentés exactement en binaire, donc leur somme est à un cheveu de 0.3. Comparez avec une tolérance, jamais avec ==.
La trigonométrie
Les fonctions trigonométriques travaillent en radians, pas en degrés - l'autre source fiable de mauvaises réponses.
L'ensemble complet : sin, cos, tan, leurs inverses asin, acos, atan, la version à deux arguments atan2(y, x), et la famille hyperbolique sinh, cosh, tanh. Préférez atan2(y, x) à atan(y / x) pour les angles - elle gère x == 0 et trouve le bon quadrant.
math.h définit M_PI sur la plupart des systèmes, mais c'est une extension POSIX plutôt que du C standard, elle peut donc manquer sous -std=c17. Définir votre propre constante, comme ci-dessus, est portable.
Logarithmes et exponentielles
Notez le piège de nommage hérité des mathématiques : log() est le logarithme naturel (base e), pas le logarithme décimal. La base 10, c'est log10().
log(0.0) renvoie moins l'infini et log(-1.0) renvoie NaN - aucune des deux ne plante, ce qui signifie qu'un mauvais argument voyage silencieusement dans votre calcul jusqu'à ce que quelque chose affiche nan.
INFINITY, NAN, et comment les tester
Le flottant possède des valeurs qui ne sont pas des nombres, et math.h les nomme :
Cette dernière ligne est cruciale. Vous ne pouvez pas écrire if (x == NAN) - c'est toujours faux. Utilisez isnan(x). (L'identité x != x n'est vraie que pour NaN, c'est ainsi que isnan est souvent implémentée et un repli pratique si vous rencontrez un jour une chaîne d'outils sans elle.)
Les infinis viennent d'un débordement (exp(1000.0)) et de la division d'un double non nul par zéro qui - contrairement à la division entière par zéro - est définie et ne plante pas.
Les variantes float et long double
Chaque fonction a trois formes : la version double, une version float suffixée par f, et une version long double suffixée par l.
float sqrtf(float x);
double sqrt(double x);
long double sqrtl(long double x);
Utilisez les versions double simples sauf raison mesurée de faire autrement - double est le type vers lequel le C promeut par défaut, et mélanger des variantes float coûte généralement plus en conversions que cela n'économise. Le seul endroit où le suffixe compte est un calcul purement float critique en performance sur du matériel sans prise en charge de la double précision.
Un exemple pratique
En combinant plusieurs d'entre elles - la distance entre deux points et les racines d'un trinôme :
Remarquez fabs(disc) < 1e-12 plutôt que disc == 0.0 - la même règle de tolérance que précédemment, appliquée là où un discriminant calculé à partir de valeurs mesurées ne tombera presque jamais exactement sur zéro.
<math.h> est un en-tête dans une boîte à outils plus vaste ; la page la bibliothèque standard cartographie le reste, et les nombres aléatoires couvrent rand, qui vit dans <stdlib.h> plutôt qu'ici.
Questions fréquentes
Comment utiliser les fonctions mathématiques en C ?
Ajoutez #include <math.h> en haut du fichier et appelez-les : sqrt(16.0), pow(2.0, 10.0), fabs(-3.5). Elles prennent et renvoient des double. Sous Linux, vous devez aussi lier la bibliothèque mathématique avec -lm : gcc program.c -o program -lm.
Pourquoi ai-je « undefined reference to sqrt » à la compilation ?
L'en-tête a donné la déclaration au compilateur, mais l'implémentation vit dans une bibliothèque séparée que l'éditeur de liens n'intègre pas automatiquement sous Linux. Ajoutez -lm à la fin de la commande : gcc program.c -o program -lm. Sous macOS et avec MinGW sous Windows, le code mathématique fait partie de la bibliothèque C standard, aucune option n'est donc nécessaire.
Quelle est la différence entre abs et fabs en C ?
abs() de <stdlib.h> prend un int et renvoie un int. fabs() de <math.h> prend un double et renvoie un double. Appeler abs(-3.7) convertit d'abord l'argument en int et donne 3, jetant silencieusement la fraction - utilisez fabs pour les valeurs flottantes.
Comment tester un NaN en C ?
Utilisez isnan(x) de <math.h>. Vous ne pouvez pas tester avec x == NAN, car NaN se compare comme différent de tout, y compris de lui-même - cette bizarrerie est justement le test de repli : x != x n'est vrai que pour NaN. Utilisez isinf(x) pour les infinis et isfinite(x) pour vérifier qu'on a un nombre ordinaire.