
La limite du silicium : pourquoi les calculs en virgule flottante et entiers échouent silencieusement
- 16 avril 2026
- 8 mins de lecture
- Concepts de programmation , Qualité logicielle
Table des matières
J’ai récemment développé une interface de métriques d’exécution pour suivre les performances d’une application.
J’ai choisi le type float pour stocker les valeurs, car il semblait suffisant pour représenter des nombres décimaux.
Lorsque nous avons vérifié les données, les résultats étaient faux. Ils ne correspondaient pas aux résultats attendus. Nous avons supposé que la logique de calcul était défectueuse et avons passé des heures à déboguer le code de calcul.
Les résultats étaient également incohérents. Parfois les valeurs étaient correctes. D’autres fois, elles dérivaient progressivement de la réalité.
Nous avons finalement trouvé la cause : la précision limitée du type float.
Il ne disposait pas d’une précision suffisante pour notre plage de valeurs.
La correction a été simple : nous sommes passés à double, et les erreurs ont disparu.
Choisir le mauvais type de données est un tueur silencieux en logiciel. Cela ne fait pas toujours planter votre programme. À la place, cela corrompt lentement vos données.
Ces problèmes apparaissent souvent sous forme d’erreurs de précision en virgule flottante ou de dépassement d’entier, qui peuvent tous deux altérer silencieusement les calculs.
Pourquoi les petits choix comptent : Ariane et Boeing
Ce n’est pas simplement un petit bug. L’histoire montre que les erreurs numériques peuvent être catastrophiques.
- La fusée Ariane 5 (1996) : Un nombre en virgule flottante sur 64 bits a été converti en entier signé sur 16 bits. La valeur était trop grande. Cela a provoqué un dépassement. La fusée s’est auto-détruite 37 secondes après le décollage. Lire le rapport complet ici.
- Le bug électrique du Boeing 787 : Un compteur logiciel utilisait un entier signé sur 32 bits pour mesurer le temps en centisecondes. Si l’avion restait alimenté pendant 248 jours, le compteur débordait. Cela pouvait entraîner une coupure totale de l’alimentation électrique en plein vol. Voir la directive de la FAA.
Cas 1 : Les entiers et leur limite stricte
Les entiers sont stockés sur un nombre fixe de bits. Un signed int en C utilise généralement 32 bits. Il possède une valeur minimale et maximale fixes.
Représentation : Les entiers utilisent un format binaire direct (souvent le complément à deux pour les valeurs signées). Il n’y a pas de fraction.
Dépassement (overflow / underflow) : Lorsqu’un entier atteint sa limite, il “reboucle”.

Exemple :
1#include <stdio.h>
2
3int main() {
4 unsigned char max_val = 255;
5 printf("Max: %hhu\n", max_val);
6 printf("Overflow: %hhu\n", max_val + 1); // Devient 0
7 return 0;
8}
La sortie est :
Max: 255
Overflow: 0
Nous utilisons ici un entier non signé sur 8 bits (unsigned char en C), la valeur maximale est 255 (2^8 - 1).
- Le calcul :
255 + 1
Mais attention : le registre ne contient que 8 bits.
Ce 1 en tête n’a nulle part où aller. Il est reporté dans un drapeau (flag) du processeur, et le registre conserve 00000000.
Un comportement similaire apparaît lors d’une soustraction, appelé underflow.
Par exemple, si vous soustrayez 1 à 0, vous obtenez 255 avec un entier non signé sur 8 bits.
Pourquoi les entiers signés sont plus dangereux
Avec les entiers signés, le bit le plus à gauche est le bit de signe (0 pour positif, 1 pour négatif). Si vous dépassez la limite d’un nombre positif, vous basculez ce bit. Un grand montant positif peut alors devenir une dette massive.
1#include <stdio.h>
2
3int main() {
4 signed char max_val = 127;
5 printf("Max: %hhd\n", max_val);
6 printf("Overflow: %hhd\n", max_val + 2); // Devient -127
7 return 0;
8}
La sortie est :
Max: 127
Overflow: -127
- Le calcul :
127 + 2
en complément à deux correspond à -127.
Les entiers rebouclent lorsqu’ils dépassent leurs limites.
Gardez cela en tête lors de calculs arithmétiques.
Vérifiez toujours vos bornes et utilisez des types plus larges si nécessaire.
Cas 2 : Les flottants et la cible mouvante
Les nombres en virgule flottante sont plus complexes.
Représentation : Un float est composé de trois parties :
- Bit de signe : positif ou négatif.
- Exposant : définit la plage de valeurs (l’échelle).
- Mantisse (ou significande) : définit la précision (les chiffres).

Ils suivent généralement la norme IEEE 754. Il existe aussi des formats plus petits, comme les minifloats, utilisés en machine learning et en infographie pour des raisons d’efficacité.
Les deux types de problèmes :
- Dépassement vers l’infini : le nombre devient trop grand pour l’exposant et se transforme en
inf(infini). - Perte de précision : le nombre reste dans la plage, mais la mantisse est trop petite pour représenter les variations. C’est ce qui s’est produit dans mon interface de métriques.
1#include <stdio.h>
2#include <float.h>
3
4int main() {
5 // 1. Dépassement de l’exposant
6 float big = FLT_MAX;
7 printf("Max Float: %e\n", big);
8 printf("Overflow to Inf: %e\n", big * 2.0f);
9
10 // 2. "Overflow" de précision (erreur silencieuse)
11 float x = 16777216.0f; // 2^24
12 printf("Original: %f\n", x);
13 printf("Add 1.0: %f\n", x + 1.0f); // Affiche toujours 16777216.000000
14
15 printf("Max Float and precision: %e\n", big + 1000.0f);
16
17 return 0;
18}
La sortie est :
Max Float: 3.402823e+38
Overflow to Inf: inf
Original: 16777216.000000
Add 1.0: 16777216.000000
Max Float and precision: 3.402823e+38
Dans le second cas, la valeur est tellement grande que l’ajout de 1.0 est perdu, car la mantisse ne peut pas représenter ce niveau de détail.
L’écart entre les nombres représentables augmente de façon exponentielle avec l’exposant.
Fait important : la perte de précision et le dépassement vers l’infini ne sont pas exclusifs.
Ajouter un petit nombre à FLT_MAX entraîne généralement une perte de précision ; la valeur peut rester FLT_MAX tant que l’ajout n’est pas assez grand pour provoquer un dépassement vers l’infini.
Distribution des nombres représentables
Pour illustrer cela, visualisons la distribution des nombres pour un float sur 8 bits.
Considérons les formats S1E4M3 (4 bits d’exposant, 3 bits de mantisse) et S1E3M4 (3 bits d’exposant, 4 bits de mantisse).
| Caractéristique | Float S1E4M3 | Float S1E3M4 |
|---|---|---|
| Bits d’exposant | 4 | 3 |
| Bits de mantisse | 3 | 4 |
| Nombres distincts | 239 | 223 |
| Valeur min | -240.0 | -15.5 |
| Valeur max | 240.0 | 15.5 |
| Point fort | Large plage | Haute précision |
En excluant -inf, inf et NaN, S1E4M3 peut représenter 239 nombres distincts entre -240.0 et 240.0, tandis que S1E3M4 peut en représenter 223 entre -15.5 et 15.5.
Cependant, S1E3M4 offre plus de précision pour les petites valeurs grâce à sa mantisse plus large, tandis que S1E4M3 couvre une plage plus large mais avec moins de précision.
Visualisons maintenant la distribution pour S1E4M3.

Aucune valeur n’existe entre les pics. Plus on s’éloigne de zéro, plus les valeurs deviennent espacées.
À partir de la valeur 16.0, nous ne pouvons plus représenter tous les entiers car l’écart entre deux valeurs représentables devient supérieur à 1.
Ce seuil vient de :
Par exemple, 17.0 ne peut pas être représenté.
C’est exactement le problème rencontré dans mon interface de métriques.

Gérer le “rien”
Les floats possèdent un état spécial appelé NaN (Not a Number). Il apparaît lors d’opérations indéfinies, comme ou la racine carrée d’un nombre négatif. Vous pouvez en apprendre plus sur NaN sur Wikipedia.
Les entiers gèrent ces situations différemment.
Ils ne disposent pas d’un état “Not a Number” (NaN).
Si vous effectuez une opération invalide comme une division par zéro, le comportement est indéfini et peut provoquer un crash du programme.
Ils doivent toujours représenter un motif binaire valide dans leur plage.
Bulletin d'information
Abonnez-vous à notre bulletin d'information et restez informé(e).
Conclusion
Vous devez comprendre les besoins de vos types numériques : la plage de valeurs à représenter ainsi que la perte de précision acceptable.
- Utilisez des entiers lorsque vous avez besoin de valeurs exactes et que vous pouvez prévoir la plage maximale.
- Utilisez double (64 bits) par défaut pour les nombres décimaux. N’utilisez float (32 bits) ou les minifloats que si vous avez des contraintes mémoire fortes et que vous avez vérifié que la perte de précision est acceptable.
- Lors des opérations arithmétiques, testez toujours les cas limites, en particulier aux frontières des types.
Si vous utilisez des nombres en virgule flottante, faites attention à la diminution exponentielle de la précision lorsque les valeurs augmentent.
Vous pouvez approfondir la distribution et la précision des floats dans l’explication visuelle de Fabien Sanglard.


