Calculer une puissance en C++ : méthodes, fonctions et bonnes pratiques

puissance en c++

pow(10, 2) peut retourner 99.999999999999999 au lieu de 100. Sur un compilateur, ça marche. Sur un autre, ça plante silencieusement. C’est le genre de bogue qui passe en revue de code et explose en production.

Calculer une puissance en C++ semble trivial – ça ne l’est pas dès que vous travaillez avec des entiers ou que la performance compte.

C’est quoi pow en C++ et comment l’utiliser?

pow() est la fonction standard pour élever un nombre à une puissance. En C++, elle est définie dans l’en-tête <cmath> ; en C pur, vous l’incluez via <math.h>. Son prototype de base est le suivant :

double pow(double base, double exposant);

Elle prend deux double et retourne un double. Voici un exemple minimal :

#include <cmath>
#include <iostream>

int main() {
    double result = pow(2.0, 8.0); // 256.0
    std::cout << result << std::endl;
    return 0;
}

Deux variantes ont été ajoutées dans C99 : powf() qui opère sur des float, et powl() qui travaille sur des long double. En C++, pow est également surchargée dans <complex> pour les nombres complexes, et dans <valarray> pour les tableaux de valeurs. Ces surcharges sont transparentes si vous utilisez les bons types.

Les normes C et C++ derrière la fonction pow

pow existe depuis les premières heures du C standardisé. La norme C89, introduite par l’ANSI, puis sa version internationale C90 publiée par l’ISO sous la référence ISO/IEC 9899-1990, incluent toutes deux pow avec le même prototype double pow(double, double). Le comportement en cas d’erreur de domaine, lui, varie selon la norme.

C99 (ISO/IEC 9899:1999) a ajouté les variantes powf et powl, précisé la gestion des cas limites via errno et HUGE_VAL, et renforcé la conformité IEEE 754. C11 (ISO/IEC 9899:2011) a légèrement affiné ces spécifications, avant d’être lui-même remplacé par C17 (ISO/IEC 9899:2018).

La différence entre C et C++ est structurelle. En C pur, pow prend toujours deux double – point final. En C++, le compilateur peut sélectionner la bonne surcharge selon le type des arguments : pow(2.0f, 3) appellera powf de façon implicite. Cette résolution automatique est pratique, mais elle masque parfois des conversions implicites qui coûtent de la précision.

Pourquoi pow() peut donner des résultats incorrects avec les entiers?

puissance en langage c

pow repose sur l’arithmétique flottante IEEE 754. Elle calcule base^exposant en interne via exp(exposant * log(base)), ce qui introduit des erreurs d’arrondi. Pour pow(10, 2), le résultat peut être 99.999999999999999 au lieu de 100.

Quand vous castez ce résultat en int, la troncature vous donne 99. Le bogue est silencieux et dépend du compilateur et du niveau d’optimisation – ce qui le rend encore plus difficile à reproduire.

Trois cas limites méritent votre attention :

  • Base zéro avec exposant négatif ou nul : erreur de domaine, comportement indéfini selon la norme.
  • Base négative avec exposant non entier : résultat NaN, car le logarithme d’un nombre négatif n’est pas défini dans les réels.
  • Exposants très grands : dépassement de capacité du double, retourne HUGE_VAL.

La correction classique consiste à ajouter un epsilon avant le cast : (int)(pow(5, 2) + 1e-9). Ce décalage de 1e-9 absorbe l’erreur d’arrondi typique sans fausser le résultat pour les grandes valeurs. Cette technique fonctionne quel que soit le compilateur, mais elle ne règle pas les problèmes de domaine ni les exposants non entiers.

Comment calculer une puissance en C++ sans pow?

Pour des exposants entiers positifs, une boucle simple fait le travail avec une complexité linéaire O(b), où b est l’exposant :

long long puissance(long long base, int exp) {
    long long resultat = 1;
    for (int i = 0; i < exp; i++) {
        resultat *= base;
    }
    return resultat;
}

Avec un exposant de 10, vous effectuez 10 multiplications. C’est prévisible, exact sur les entiers, et sans aucune dépendance à <cmath>. Cette approche reste valable en C pur avec <math.h> quand vous souhaitez éviter la conversion implicite vers double.

Pour les exposants décimaux sans pow, vous pouvez reconstruire le calcul avec exp et log : exp(exposant * log(base)). C’est exactement ce que pow fait en interne – vous ne gagnez donc rien en précision, mais vous gardez le contrôle explicite sur chaque étape du calcul.

Calculer une puissance de 10 en C++ : quelle approche choisir?

La puissance de 10 est un cas particulièrement fréquent : conversions d’unités, formatage de nombres, calculs d’index. Trois options s’offrent à vous selon le contexte.

Méthode Exemple Adapté pour Risque
pow(10, n) pow(10, 3) → 1000.0 Exposant variable, résultat flottant Arrondi si cast en int
Multiplication directe 10 10 10 Exposant connu à la compilation Verbeux au-delà de 4-5
Constante littérale 1000 ou 1e3 Valeur fixe, lisibilité du code Aucun

Si l’exposant est connu au moment de la compilation, une constante littérale ou une constexpr est toujours préférable à pow. Pour un exposant calculé à l’exécution avec un résultat entier attendu, utilisez la boucle manuelle avec le décalage epsilon, ou une table de correspondance si l’intervalle est borné (par exemple, les 19 puissances de 10 représentables en long long).

Performance : pow vs multiplication directe en C++

puissance en c sans pow

Sans optimisation agressive, std::pow(x, n) est environ 100 fois plus lent que la multiplication manuelle pour de petits exposants entiers – un écart de 2 ordres de grandeur. La raison est simple : pow appelle exp et log, deux opérations transcendantes avec du cas-limit checking, de la gestion d’erreur et une latence typique de 20 à 100 cycles selon le processeur.

La multiplication entière, elle, tient en 1 à 3 cycles sur un pipeline moderne. Pour x x x, vous dépensez moins de 10 cycles. La différence n’a aucun impact dans du code applicatif classique, mais elle devient visible dans des boucles internes de traitement numérique ou de simulation.

Le flag -ffast-math de GCC et Clang modifie la donne. Il autorise le compilateur à remplacer pow(x, 2) par x x et pow(x, 3) par x x * x automatiquement. Le code résultant est aussi rapide que la version manuelle. Mais -ffast-math désactive aussi la gestion stricte de NaN, Inf et des arrondis IEEE 754 – ce qui peut casser des calculs qui en dépendent.

La règle pragmatique : dans du code critique en performance, remplacez pow par la multiplication explicite pour les petits exposants entiers. Gardez pow pour les exposants décimaux ou variables où la précision flottante est nécessaire et assumée.

pow reste un mauvais choix pour les puissances entières en C++

pow est conçu pour des flottants. L’utiliser avec des entiers, c’est payer le coût d’une conversion implicite vers double, d’un calcul transcendant, puis d’une retroncature – avec un risque d’arrondi à chaque étape. Une fonction utilitaire constexpr résout le problème proprement :

template<typename T>
constexpr T ipow(T base, unsigned int exp) {
    T result = 1;
    while (exp--) result *= base;
    return result;
}

Cette version est évaluable à la compilation si les arguments sont des constantes, typée correctement grâce au template, et sans aucun appel flottant. Elle ne gère pas les exposants négatifs, mais les puissances entières négatives retournent de toute façon des fractions – si vous en avez besoin, le type de retour doit être double.

Pour choisir la bonne approche selon le contexte :

  • Exposant entier positif, résultat entier : utilisez ipow ou la multiplication directe.
  • Exposant décimal ou résultat flottant : pow est approprié, avec gestion des cas limites.
  • Exposant variable et résultat entier : boucle manuelle avec epsilon ou table précalculée.
  • Puissance de 10 fixe : constante littérale, toujours.

pow ne disparaîtra pas de votre code – mais chaque fois que vous l’utilisez avec deux entiers, vous faites confiance à un mécanisme flottant pour produire un résultat exact. Cette confiance est rarement justifiée.