Factorielle en C - L'approche fiable sans débordement

Alfred Jacques .

3 mars 2026

Graphiques montrant la variance expliquée par axe factoriel. Le premier graphique affiche la variance par axe, le second la variance cumulée. Utile pour une analyse factorielle en C.

Calculer la factorielle en C est un excellent exercice pour comprendre à la fois les boucles, la récursion et les limites des types entiers. Je vais partir du cas concret: écrire un programme correct, lisible et capable de signaler quand le résultat ne tient plus dans une variable classique. Vous verrez aussi pourquoi je privilégie souvent une boucle dans un vrai projet, même si la version récursive reste très utile pour apprendre.

Ce qu’il faut garder en tête avant de coder

  • 0! vaut 1 et les valeurs négatives ne donnent pas de factorielle valide.
  • La version la plus fiable pour débuter est souvent une boucle, pas la récursion.
  • La factorielle grossit très vite: sur une plateforme courante, 12! tient dans un `int` signé 32 bits, mais pas 13!.
  • Avec un entier 64 bits classique, on va généralement jusqu’à 20!; 21! déborde déjà.
  • Si vous voulez des résultats exacts au-delà, il faut une vérification de dépassement ou une bibliothèque de grands entiers.

Ce que la factorielle demande vraiment

La règle mathématique est simple: pour un entier `n` positif, `n! = 1 × 2 × 3 × ... × n`. En pratique, le premier réflexe consiste à traiter correctement trois cas: `n = 0`, `n = 1` et `n < 0`. La première erreur que je vois souvent, c’est d’oublier que 0! = 1 et non `0`, ce qui casse très vite un programme censé être rigoureux.

Il faut aussi accepter une réalité moins confortable: la factorielle explose. Même avec des types entiers modernes, on atteint vite la limite. C’est pour cela que le sujet n’est pas seulement “comment écrire une formule”, mais aussi “comment protéger le programme contre les entrées qui dépassent la capacité du type choisi”. Une fois ce point clair, la bonne implémentation devient beaucoup plus évidente.

  • `0! = 1`
  • `1! = 1`
  • Pour `n < 0`, le résultat n’est pas défini dans le cadre classique du programme entier
  • La croissance est très rapide, donc le choix du type compte autant que l’algorithme

Avec cette base, on peut passer au code sans se tromper sur le sens du calcul, puis décider si une boucle ou une fonction récursive est la meilleure forme pour votre cas.

La version la plus simple avec une boucle

Pour un programme C propre et prévisible, je pars presque toujours d’une boucle. Elle est lisible, elle évite la pile d’appels, et elle permet de contrôler le débordement avant qu’il ne produise un résultat faux. C’est la version que je garderais pour un utilitaire, un petit outil interne ou un exercice à rendre sans artifices.

#include 
#include 

int main(void) {
    long long n;
    unsigned long long resultat = 1;

    printf("Entrez un entier positif : ");
    if (scanf("%lld", &n) != 1 || n < 0) {
        printf("Entrée invalide\n");
        return 1;
    }

    for (long long i = 2; i <= n; ++i) {
        if (resultat > ULLONG_MAX / (unsigned long long)i) {
            printf("Dépassement de capacité\n");
            return 1;
        }
        resultat *= (unsigned long long)i;
    }

    printf("%lld! = %llu\n", n, resultat);
    return 0;
}

Ce code fait trois choses importantes que je considère comme non négociables: il valide l’entrée, il calcule itérativement, et il bloque avant le débordement. Le test `resultat > ULLONG_MAX / i` est la partie la plus intéressante, parce qu’il vérifie le risque avant la multiplication. Autrement dit, on ne laisse pas le programme produire une valeur corrompue puis essayer de la corriger après coup.

Dans une logique de production, c’est souvent ce que je recommande. La prochaine question naturelle est alors de savoir si la récursion apporte quelque chose de plus élégant ou de plus pédagogique.

La version récursive et ce qu’elle implique

La version récursive est plus courte et plus proche de la définition mathématique. Elle convient très bien pour illustrer l’idée de base case et de recursive case, c’est-à-dire le cas d’arrêt et l’appel du sous-problème. En revanche, elle n’est pas la solution la plus robuste si vous comptez traiter des entrées élevées ou des contextes où la profondeur d’appel doit rester maîtrisée.

unsigned long long factorielle(unsigned int n) {
    if (n < 2) {
        return 1;
    }
    return n * factorielle(n - 1);
}

Ici, `n < 2` joue le rôle de cas d’arrêt. Sans lui, la fonction s’appellerait elle-même indéfiniment jusqu’à épuiser la pile. Chaque appel crée son propre cadre d’exécution, avec ses variables et son retour en attente. C’est pour cela que la récursion finit par consommer de la mémoire de pile, même si le code semble minuscule à l’écran.

Je précise aussi un point qui évite bien des malentendus: en C, il ne faut pas compter sur une optimisation terminale pour transformer automatiquement cette fonction en boucle. Même quand le compilateur sait parfois l’appliquer, ce n’est pas une garantie de conception. Si vous voulez une récursion terminale, il faut l’écrire explicitement avec un accumulateur, mais pour une factorielle je trouve souvent la boucle plus nette et plus sûre.

Cette version a donc surtout de la valeur pour apprendre et pour expliquer la mécanique de l’appel de fonction. Dès qu’on passe à l’exécution réelle, la vraie limite devient celle des entiers, pas celle de la syntaxe.

Le vrai piège est le débordement numérique

Sur une machine moderne, le problème n’est pas de savoir multiplier, mais de savoir jusqu’où les multiplications restent exactes. La factorielle grandit si vite qu’un type trop petit donne un résultat faux alors que le programme a l’air de fonctionner. C’est l’un des pièges les plus classiques en C, et franchement l’un des plus trompeurs pour un débutant.

Type courant Capacité pratique sur une plateforme classique Point de rupture
`int` signé 32 bits jusqu’à `12!` `13!` déborde
`unsigned long long` 64 bits jusqu’à `20!` `21!` déborde
`uintmax_t` dépend de l’implémentation, souvent 64 bits ou plus utile si votre environnement propose un entier natif plus large
Bibliothèque de grands entiers résultats bien au-delà des types natifs à choisir si vous voulez un calcul exact pour de grandes valeurs

Pour moi, la bonne règle est simple: si vous voulez rester en entier natif, posez une limite explicite. Si vous voulez aller plus loin, changez d’outil au lieu d’espérer qu’un type standard suffira. C’est aussi pour cela que je garde souvent une vérification du type `if (resultat > ULLONG_MAX / i)` avant chaque multiplication.

Un autre point utile: changer l’ordre des calculs ne résout pas le problème. La factorielle ne se “réorganise” pas magiquement pour éviter le débordement. Dès que la valeur cible dépasse la capacité du type, il faut soit arrêter proprement, soit passer à une représentation plus large. C’est cette contrainte qui détermine ensuite le choix de l’implémentation la plus adaptée.

Ce que je ferais dans un vrai projet C

Si je dois livrer un code propre, je choisis la méthode en fonction du but réel, pas en fonction de la beauté théorique de l’algorithme. Pour un exercice pédagogique, je montre la récursion. Pour un programme simple, je garde la boucle. Pour un calcul exact au-delà des limites natives, je change d’outillage. C’est la manière la plus honnête de traiter ce sujet.

Besoin Approche que je retiens Pourquoi
Apprendre la récursion Version récursive Elle montre très bien le cas de base et la pile d’appels
Calcul rapide et lisible Version itérative Moins de risque, plus de contrôle, comportement prévisible
Entrées potentiellement grandes Validation + contrôle de dépassement Le programme refuse proprement ce qu’il ne peut pas représenter
Résultats exacts très grands Bibliothèque de grands entiers Les types natifs ne suffisent plus

Mon réflexe, dans la pratique, est de documenter la limite maximale acceptée par le programme et de la faire respecter explicitement. C’est plus utile qu’un code “général” qui affiche un nombre faux sans prévenir. Si vous retenez une seule idée, gardez celle-ci: en C, la difficulté n’est pas de calculer la factorielle, mais de la calculer sans mentir sur le résultat.

Questions fréquentes

La définition mathématique de la factorielle est un produit décroissant. Pour que la formule n! = n * (n-1)! reste cohérente, et pour des raisons combinatoires (nombre de façons d'ordonner un ensemble vide), 0! est défini comme étant 1.
L'implémentation itérative (avec une boucle) calcule le résultat pas à pas, consommant moins de mémoire de pile. La version récursive s'appelle elle-même jusqu'à un cas de base, étant plus proche de la définition mathématique mais potentiellement plus gourmande en ressources pour de grandes valeurs.
Avec un `int` 32 bits, 12! est la limite. Avec un `unsigned long long` 64 bits, vous pouvez aller jusqu'à 20!. Au-delà, un débordement numérique se produit, rendant le résultat incorrect. Il faut alors utiliser des bibliothèques de grands entiers.
Il est crucial de vérifier la capacité du type avant chaque multiplication. Par exemple, `if (resultat > ULLONG_MAX / i)` permet de détecter un débordement imminent et d'arrêter le calcul ou de signaler une erreur avant que le résultat ne soit corrompu.

Évaluer l'article

Moyenne: 0.0 / 5 · 0 évaluations

Tags

factorielle en c calcul factorielle c débordement factorielle c unsigned long long factorielle récursive c limites
Autor Alfred Jacques
Alfred Jacques
Je m'appelle Alfred Jacques et je suis passionné par les technologies, en particulier dans les domaines du web, de l'intelligence artificielle, des réseaux et de la sécurité. Fort de plusieurs années d'expérience en tant qu'analyste de l'industrie, j'ai eu l'opportunité d'explorer en profondeur les tendances et les innovations qui façonnent notre monde numérique. Mon expertise se concentre sur l'analyse des systèmes de sécurité, l'impact de l'IA sur les entreprises et l'évolution des infrastructures web. Je m'efforce de simplifier des données complexes pour les rendre accessibles à tous, tout en garantissant une analyse objective et rigoureuse. Mon engagement envers mes lecteurs est de fournir des informations précises, à jour et fiables, afin de les aider à naviguer dans cet écosystème technologique en constante évolution.

Commentaires (0)

Ajouter un commentaire