← Retour au blogSystèmes de numération

Virgule flottante IEEE 754 : des bits aux nombres

Décodez les formats IEEE 754 binary32 et binary64 : signe, exposant biaisé, significande de queue, valeurs spéciales, arrondi et arithmétique non associative.

DennyDenny
10 min de lecture
Chalkboard feature illustration for “Virgule flottante IEEE 754 : des bits aux nombres”

En bref : pour une valeur binaire normale, IEEE 754 emploie (−1)s(1+f)2e−bias(-1)^s(1+f)2^{e-bias} : un bit de signe, un exposant biaisé et un champ de significande de queue dont le 1 initial est implicite. Des motifs d’exposant réservés codent les zéros signés, les nombres sous-normaux, les infinis et les NaN. Comme la plupart des résultats réels exacts doivent être arrondis vers un format fini, l’addition et la multiplication en virgule flottante ne sont généralement pas associatives.

Le binaire à virgule fixe convient lorsque l’étendue et l’échelle sont connues. Il devient peu pratique dès qu’un même calcul doit couvrir des valeurs séparées par de nombreux ordres de grandeur. Une valeur signée de 32 bits à virgule fixe, dont 16 bits fractionnaires, possède par exemple un pas de 2−162^{-16} et une plage d’environ −32,768-32{,}768 à 32,767.9999832{,}767.99998.

La virgule flottante attribue des bits à un exposant, ce qui permet au point binaire de se déplacer selon la grandeur de la valeur. IEEE 754 fut publié pour la première fois en 1985 ; sa révision active est IEEE 754-2019. La norme définit des formats binaires et décimaux, des opérations, des arrondis, des exceptions et le comportement d’échange. Une implémentation peut être matérielle, logicielle ou combiner les deux : la conformité n’implique donc pas que chaque processeur réalise chaque format dans le silicium.

Pourquoi la virgule flottante ?

Sur le papier, la notation scientifique résout le même problème : 6.674×10−116.674 \times 10^{-11} sépare une petite significande d’un grand exposant. IEEE 754 est une notation scientifique binaire assortie de règles strictes sur le nombre de bits attribué à chaque partie et sur leur codage.

Deux impératifs façonnent le format :

  1. Des codages et des opérations spécifiés. Il n’existe pas exactement un motif de bits par valeur mathématique : zéro possède deux signes et les NaN de nombreux codages de charge utile. La norme définit la manière dont les opérations et les comparaisons les traitent.
  2. Une arithmétique réalisable. La comparaison des exposants et l’alignement des significandes doivent se traduire en algorithmes matériels ou logiciels finis, avec des comportements d’arrondi et d’exception spécifiés.

Les compromis qui satisfont ces deux impératifs — exposants biaisés, bits initiaux implicites et codes d’exposant réservés — donnent à IEEE 754 sa forme caractéristique.

Organisation en simple précision (32 bits)

| 31 | 30 ............ 23 | 22 ........................... 0 |
|  s |       e (8 bits)   |          m (23 bits)             |
| 1  |                    |                                  |
  • Bit de signe ss : 1 bit. 0 signifie non négatif, 1 signifie négatif.
  • Exposant ee : 8 bits, enregistrés comme un entier non signé avec un biais de 127.
  • Champ de significande de queue mm : 23 bits. Pour les nombres normaux, il stocke la fraction qui suit un 1 initial implicite.

Pour les nombres normalisés — le cas courant —, la valeur est :

v=(−1)s⋅(1+m223)⋅2e−127v = (-1)^s \cdot \left(1 + \frac{m}{2^{23}}\right) \cdot 2^{e - 127}

Le « 1.1. » de la significande est implicite : le format économise un bit en exploitant le fait que tout nombre binaire normalisé peut s’écrire avec un 1 initial grâce au choix de l’exposant.

Organisation en double précision (64 bits)

La structure reste identique, mais les champs sont plus larges :

| 63 | 62 ........... 52 | 51 ............................. 0 |
|  s |     e (11 bits)   |          m (52 bits)              |
  • Bit de signe ss : 1 bit.
  • Exposant ee : 11 bits, biais 1023.
  • Champ de significande de queue mm : 52 bits.

Valeur normalisée :

v=(−1)s⋅(1+m252)⋅2e−1023v = (-1)^s \cdot \left(1 + \frac{m}{2^{52}}\right) \cdot 2^{e - 1023}

Étendue : environ ±1.8×10308\pm 1.8 \times 10^{308}, avec quelque 15 à 17 chiffres décimaux significatifs.

Pourquoi biaiser l’exposant ?

Stocker l’exposant sous la forme e−biase - bias plutôt qu’en complément à deux apporte un avantage subtil mais important : la comparaison de nombres flottants se ramène à une comparaison entière de leurs motifs de bits, après inversion du bit de signe. Si deux flottants non négatifs sont comparés comme des entiers non signés de 32 bits, le plus grand flottant correspond au plus grand entier. Des exposants en complément à deux rompraient cette relation monotone à la frontière entre exposants positifs et négatifs.

Le champ d’exposant entièrement nul est réservé aux zéros et aux nombres sous-normaux, tandis que le champ entièrement composé de 1 est réservé aux infinis et aux NaN. En binary32, les champs d’exposant normaux de 1 à 254 correspondent aux exposants non biaisés de −126-126 à +127+127.

Les cas particuliers

Bits d’exposantBits de significande de queueSignification
Tous à zéroTous à zéro±0\pm 0 (le bit de signe les distingue)
Tous à zéroNon nulsNombre sous-normal
Valeur intermédiaireValeur quelconqueNombre normalisé
Tous à unTous à zéro±∞\pm \infty
Tous à unNon nulsNaN (Not a Number)

Zéro possède deux codages, +0+0 et −0-0. Ils sont égaux selon la comparaison IEEE, mais produisent des résultats de signes différents dans certaines opérations, par exemple 1/+0=+∞1 / +0 = +\infty et 1/−0=−∞1 / -0 = -\infty.

Les nombres sous-normaux, historiquement appelés aussi dénormalisés, comblent l’intervalle entre zéro et le plus petit nombre normal. Le 1 initial implicite devient 0 et binary32 emploie un exposant effectif de −126-126. On obtient ainsi un sous-dépassement graduel : l’espacement absolu reste constant dans la plage sous-normale, tandis que la précision relative diminue à l’approche de zéro.

L’infini résulte d’un dépassement ou d’une division par zéro. Il se propage dans l’arithmétique de manière intuitive : ∞+1=∞\infty + 1 = \infty, mais ∞×0=NaN\infty \times 0 = \text{NaN}.

Un NaN signale une opération invalide : −1\sqrt{-1}, 0/00 / 0 ou ∞−∞\infty - \infty. Il est contagieux : toute opération arithmétique impliquant un NaN donne un NaN. Point essentiel, NaN≠NaN\text{NaN} \neq \text{NaN} dans les comparaisons IEEE 754 ; c’est pourquoi les suites de tests emploient isnan() plutôt que ==.

Exemple détaillé : décoder 0x40490FDB

Voici une célèbre constante de 32 bits. En binaire :

0x40490FDB = 0100 0000 0100 1001 0000 1111 1101 1011

Séparons les champs :

  • Signe : 0 — positif
  • Exposant : 1000 0000 = 12810128_{10} — exposant réel 128−127=1128 - 127 = 1
  • Significande de queue : 100 1001 0000 1111 1101 1011

La significande est 1.(trailing bits)21.\text{(trailing bits)}_2, soit :

1.1001001000011111101101121.10010010000111111011011_2

Calculons la fraction stockée :

mfrac=0b10010010000111111011011223=4,788,1878,388,608≈0.5707963m_{frac} = \frac{0b10010010000111111011011}{2^{23}} = \frac{4{,}788{,}187}{8{,}388{,}608} \approx 0.5707963

La significande vaut donc 1.57079631.5707963. Multiplions-la par 212^1 :

v≈3.1415927v \approx 3.1415927

C’est π\pi avec la précision du format simple. La constante 0x40490FDB est le codage canonique IEEE 754 simple précision de π\pi ; elle apparaît dans les bibliothèques numériques, les nuanceurs GPU et les sujets d’examen du monde entier.

Exemple détaillé : coder 5,75

Convertissons 5.755.75 au format simple précision IEEE 754.

Étape 1 : conversion en binaire. 5=10125 = 101_2. 0.75=0.5+0.25=0.1120.75 = 0.5 + 0.25 = 0.11_2. En combinant les deux : 5.75=101.1125.75 = 101.11_2.

Étape 2 : normalisation. Déplaçons le point binaire vers la gauche jusqu’à ne conserver qu’un seul chiffre devant lui :

101.112=1.01112×22101.11_2 = 1.0111_2 \times 2^2

L’exposant réel vaut donc 22.

Étape 3 : codage de chaque champ.

  • Signe : le nombre est positif, donc s=0s = 0.
  • Exposant : exposant réel 22 + biais 127127 = 129129 = 1000 0001.
  • Significande de queue : supprimons le 1 initial et stockons les 23 bits suivants, soit 0111 suivi de 19 zéros, ou 01110000 00000000 0000000.

Étape 4 : assemblage.

0 | 10000001 | 01110000000000000000000

En hexadécimal : 0x40B80000. Vous pouvez le vérifier en appliquant la procédure de décodage à l’envers et en retrouvant exactement 5,75.

Remarquez que 5,75 possède une représentation binaire exacte : sa partie fractionnaire est une somme de puissances négatives de deux, 2−1+2−22^{-1} + 2^{-2}. La plupart des fractions décimales ne le peuvent pas. En simple précision, 0.10.1 devient 0x3DCCCCCD, qui se redécode en 0.1000000014901...0.1000000014901..., soit une erreur d’environ 10−910^{-9}. Cette minuscule différence est à l’origine de toutes les surprises du type « 0.1+0.2≠0.30.1 + 0.2 \neq 0.3 » que rencontrent les débutants.

Directions d’arrondi

IEEE 754-2019 nomme cinq attributs de direction d’arrondi :

  • Arrondi au plus proche, égalités vers le pair — mode par défaut. On choisit la valeur représentable la plus proche ; en cas d’égalité, celle dont le bit de significande de poids faible vaut 0.
  • Arrondi au plus proche, égalités en s’éloignant de zéro. Cet attribut est particulièrement associé à l’arithmétique décimale et n’est pas exposé par tous les environnements binaires en virgule flottante.
  • Arrondi vers zéro — troncature.
  • Arrondi vers +∞+\infty — plafond.
  • Arrondi vers −∞-\infty — plancher.

Le mode par défaut minimise le biais statistique : un simple arrondi des moitiés vers le haut introduit un léger biais positif lorsque de nombreuses valeurs sont accumulées, tandis que l’arrondi des moitiés vers le pair le compense en moyenne.

Les directions qu’une application peut sélectionner dépendent de son langage, de son environnement d’exécution, de son compilateur et de son matériel. La reproductibilité dépend également de l’ordre d’évaluation, de la précision intermédiaire, des opérations fusionnées, des fonctions de bibliothèque et du traitement des nombres sous-normaux — pas seulement de la direction d’arrondi choisie.

Pourquoi l’arithmétique flottante n’est-elle pas associative ?

Prenons trois valeurs IEEE 754 simple précision : a=1020,b=−1020,c=1a = 10^{20}, b = -10^{20}, c = 1.

(a+b)+c(a + b) + c : d’abord, a+b=0a + b = 0 exactement — l’annulation est nette. Puis 0+c=10 + c = 1. Résultat final : 11.

a+(b+c)a + (b + c) : calculons d’abord b+cb + c. L’exposant de bb avoisine 67, tandis que celui de cc vaut 0. L’alignement des significandes repousse cc bien au-delà de la précision de binary32. Après arrondi, b+c=bb+c=b. Puis a+b=0a+b=0.

Les deux parenthésages donnent des réponses différentes : l’addition en virgule flottante n’est donc pas associative. La possibilité pour un compilateur de réassocier les expressions dépend des règles du langage, de l’implémentation, des options d’optimisation et de l’environnement flottant. Le code sensible à la reproductibilité doit documenter ces choix et tester la chaîne d’outils réelle, plutôt que de considérer une option du compilateur comme une garantie universelle.

La même propriété concerne la soustraction — dans les cas d’annulation —, la multiplication — les produits extrêmement petits peuvent être arrondis à zéro — et la division — diviser par une valeur très petite peut déborder vers l’infini.

Le matériel : la FPU

Un additionneur matériel en virgule flottante réalise couramment des étapes comme celles-ci ; les processeurs dépourvus de FPU peuvent reproduire la même sémantique par logiciel :

  1. Décoder les champs des deux opérandes.
  2. Comparer les exposants ; décaler vers la droite la significande associée au plus petit exposant, tout en conservant des bits de garde, d’arrondi et collant pour l’arrondi final.
  3. Additionner ou soustraire les significandes alignées au moyen d’un large additionneur de type anticipation de retenue.
  4. Normaliser le résultat en le décalant vers la gauche jusqu’à l’apparition d’un 1 initial, et ajuster l’exposant en conséquence.
  5. Arrondir selon le mode actif.
  6. Coder le résultat au format IEEE 754.

Le chemin de données doit assurer la comparaison des exposants, l’alignement des significandes, l’addition ou la soustraction, la détection des zéros initiaux, la normalisation, l’arrondi, le traitement des cas particuliers et le signalement des exceptions. L’extension F de RISC-V définit les instructions binary32 et D ajoute binary64 ; toutes deux sont facultatives, ce qui illustre la distinction entre un format normalisé et sa réalisation matérielle obligatoire.

Le chemin de la significande réutilise les techniques de l’arithmétique binaire, mais une FPU ne se réduit pas à une ALU entière enrichie de quelques raccordements : l’arrondi, le sous-dépassement graduel, les NaN, les infinis, les zéros signés et les exceptions font partie de son comportement architectural.

Formats plus larges

Au-delà de la simple et de la double précision, IEEE 754 définit :

  • La demi-précision (binary16) : 1 bit de signe + 5 d’exposant + 10 de significande de queue. Elle est employée dans le graphisme et les calculs numériques à précision réduite.
  • La quadruple précision (128 bits) : 1 + 15 + 112. Rare, elle sert à l’analyse numérique et à certains calculs financiers.
  • bfloat16, qui n’est pas un format d’échange de base IEEE 754 : 1 + 8 + 7. Il conserve une étendue proche de binary32 avec une précision bien moindre et est largement utilisé en apprentissage automatique.

Chaque variante arbitre entre étendue et précision selon sa charge de travail cible.

Pièges courants

  • Employer == sans modèle d’erreur. La comparaison exacte convient à certains invariants et à certaines valeurs discrètes ; les résultats approximatifs nécessitent une tolérance absolue ou relative propre au domaine, ou une politique exprimée en unités de dernier rang.
  • Oublier que NaN != NaN. Employez isnan() pour détecter un NaN.
  • Supposer l’associativité. Réordonner des sommes peut changer la réponse. Certaines options du compilateur autorisent des réassociations non sûres ; comprenez-les avant de les employer.
  • Mélanger les intuitions décimale et binaire. 0.10.1 n’a aucune représentation binaire finie exacte. Pour des règles métier décimales, envisagez des entiers mis à l’échelle, la virgule flottante décimale ou le BCD, tout en définissant l’arrondi à chaque frontière où il est requis.
  • Supposer les performances des nombres sous-normaux. Certains processeurs les traitent à pleine vitesse ; d’autres subissent une pénalité ou proposent des modes de mise à zéro. Mesurez la cible et préservez le sous-dépassement graduel lorsque l’exactitude l’exige.

Relier le tout à l’architecture générale d’un processeur

L’organisation des registres dépend de l’ISA. Certaines architectures possèdent des fichiers de registres entiers et flottants séparés ; d’autres font se chevaucher ou unifient une partie de l’état. Les chargements, stockages, conversions, déplacements et conventions d’appel définissent cette frontière.

Pour l’arithmétique large, la partie entière s’appuie sur des structures apparentées à l’additionneur de 8 bits, dimensionnées et optimisées pour la cible. La partie flottante ajoute l’alignement, la normalisation, l’arrondi, les exceptions et le traitement des valeurs spéciales.

Et ensuite ?

Poursuivez avec l’explication du complément à deux pour les entiers signés de largeur fixe, puis avec les fondamentaux du décimal codé en binaire pour un codage décimal orienté chiffre.

Pour expérimenter au niveau des bits, employez un chemin de données de type ALU pour l’arithmétique de la significande, puis modélisez séparément l’alignement, la normalisation, les bits de garde, d’arrondi et collant, ainsi que les cas particuliers.

La référence normative est la norme active IEEE 754-2019 Standard for Floating-Point Arithmetic.

Transformez l’idée en un circuit fonctionnel.