Virgule fixe et IEEE-754 : plage, résolution, opérations et codage

Illustration : Virgule fixe et IEEE-754

Écrit par

dans

Sur FPGA ou sur un petit microcontrôleur sans unité flottante, on calcule en virgule fixe : un entier dont on décide où se trouve la virgule. L’outil donne la plage et la résolution d’un format, la valeur réellement codée et l’entier brut. L’onglet IEEE-754 montre comment un nombre est stocké en flottant, et pourquoi 0,1 n’y est jamais exact. L’onglet Opérations simule les quatre opérations sur des entiers, étape par étape, avec l’arrondi et le traitement des dépassements de votre choix, et génère le code C correspondant.

Notation

–

Résolution

–

Plage

–

Valeur codée

–

Entier brut

–

Virgule fixe

résolution = 2^(IWL − WL)
signé     : −2^(IWL−1)  …  2^(IWL−1) − résolution
non signé : 0  …  2^IWL − résolution
valeur = entier brut × résolution

WL est la longueur totale du mot, IWL le nombre de bits de la partie entière (bit de signe compris pour un format signé), selon la convention de LabVIEW FPGA notée <±,WL,IWL>. La notation Qm.n, courante en traitement du signal, compte m bits entiers hors signe et n bits fractionnaires : <±,16,4> correspond à Q3.12.

L’outil arrondit au plus proche avec égalité au pair (le mode par défaut de LabVIEW) et sature en cas de dépassement. En LabVIEW FPGA, la largeur du mot se paie en ressources : un multiplieur 18 × 25 bits tient dans un seul bloc DSP48 d’un FPGA Xilinx, au-delà il en faut plusieurs.

IEEE-754

FormatSigneExposantMantisseChiffres significatifs
Simple (32 bits)18 (biais 127)23≈ 7
Double (64 bits)111 (biais 1023)52≈ 16

Un flottant représente exactement les fractions dont le dénominateur est une puissance de deux. 0,1 = 1/10 n’en fait pas partie : il est stocké 0,100000001490116 en simple précision. D’où la règle : ne jamais comparer deux flottants avec une égalité stricte, mais avec une tolérance.

Les quatre opérations en virgule fixe

En virgule fixe, une opération se ramène à une opération sur les entiers bruts, suivie d’un ajustement de la position de la virgule. Le résultat exact demande en général plus de bits que les opérandes ; c’est ce que LabVIEW appelle la pleine précision.

OpérationBits fractionnaires du résultat exactBits entiersÀ retenir
a ± bmax(fa, fb)max(ia, ib) + 1aligner les virgules avant d’additionner
a × bfa + fbia + ibun produit 16 × 16 bits tient sur 32 bits
a ÷ bau choixjusqu’à ia + fb (+1 en signé)pas de résultat exact : il faut choisir la précision

Avec fa, fb le nombre de bits fractionnaires et ia, ib le nombre de bits entiers des opérandes.

Addition et soustraction

Les deux entiers doivent représenter la même puissance de deux : on décale vers la gauche celui qui a le moins de bits fractionnaires, puis on additionne. Un bit entier supplémentaire absorbe la retenue. La soustraction de deux non signés donne un résultat signé.

Multiplication

On multiplie directement les entiers : les bits fractionnaires s’ajoutent. Le produit est exact mais deux fois plus large ; pour revenir au format de départ, on le décale vers la droite, et c’est là qu’intervient l’arrondi. En C, il faut convertir un opérande vers un type plus large avant la multiplication : (int32_t)a * b, sinon le produit déborde.

Division

Le quotient de deux entiers perd sa partie fractionnaire. Pour garder f bits fractionnaires au résultat, on décale le dividende vers la gauche avant de diviser, de f − fa + fb bits. La division est l’opération la plus coûteuse : on lui préfère quand c’est possible une multiplication par l’inverse précalculé, ou un décalage pour une puissance de deux.

Arrondi et dépassement

  • Troncature : on abandonne les bits de poids faible, ce qui arrondit vers −∞. C’est gratuit, mais l’erreur moyenne vaut −½ LSB : sur un filtre ou un accumulateur, ce biais s’accumule.
  • Arrondi au plus proche : on ajoute ½ LSB avant de tronquer. L’égalité vers +∞ garde un léger biais ; l’égalité au pair le supprime, pour un peu plus de logique.
  • Saturation : un résultat hors plage est bloqué à la valeur extrême. C’est le choix sûr pour une commande ou une mesure.
  • Repliement : les bits de poids fort sont perdus, comme sur un entier ordinaire. Un grand positif devient négatif, ce qui est catastrophique sur une commande mais voulu sur un accumulateur de phase (NCO), dont le comportement modulo est exactement celui recherché.

En C et en LabVIEW FPGA

  • En C, le >> d’un entier signé négatif est arithmétique sur les compilateurs courants (garanti à partir de C23), mais le << d’un négatif est un comportement indéfini : le code généré utilise des multiplications par des puissances de deux, que le compilateur transforme en décalages.
  • La division entière du C tronque vers zéro, pas vers −∞ : −7 / 2 donne −3 en C, alors qu’une troncature en virgule fixe donne −4. Le code généré corrige ce point.
  • En LabVIEW FPGA, les mêmes choix se font dans l’onglet Output Configuration des propriétés d’une fonction numérique : décocher Adapt to source pour imposer le format, puis choisir le mode d’arrondi (Truncate, Round-Half-Up, Round-Half-Even) et de dépassement (Saturate, Wrap). Ce sont les options de l’outil. La division passe par les fonctions High Throughput Math.

Commentaires

Laisser un commentaire