Comment les nombres à virgule flottante sont-ils stockés en mémoire?

J’ai lu qu’ils sont stockés sous forme de mantisse et d’exposant

J’ai lu ce document mais je n’ai rien compris.

Pour comprendre comment ils sont stockés, vous devez d’abord comprendre ce qu’ils sont et le type de valeurs qu’ils sont censés gérer.

Contrairement aux nombres entiers, une valeur en virgule flottante est censée représenter des valeurs extrêmement petites et extrêmement grandes. Pour les valeurs à virgule flottante normales à 32 bits, cela correspond à des valeurs comsockets entre 1.175494351 * 10 ^ -38 et 3.40282347 * 10 ^ + 38 .

Clairement, en utilisant seulement 32 bits, il n’est pas possible de stocker tous les chiffres dans de tels nombres.

En ce qui concerne la représentation, vous pouvez voir tous les nombres à virgule flottante normaux sous forme de valeurs comsockets entre 1,0 et (presque) 2,0, mis à l’échelle avec une puissance de deux. Donc 1.0 est simplement 1.0 * 2 ^ 0 . 2,0 est 1,0 * 2 ^ 1 . -5.0 est -1.25 * 2 ^ 2 .

Alors, que faut-il pour encoder cela le plus efficacement possible? De quoi avons-nous vraiment besoin?

  • Le signe de l’expression
  • L’exposant
  • La valeur dans la plage de 1,0 à (presque) 2,0. Ceci est connu comme la “mantisse”, c’est-à-dire la signification.

Ceci est codé comme suit, conformément à la norme à virgule flottante IEEE-754.

  • Le signe est un seul bit.
  • L’exposant est stocké sous la forme d’un entier non signé. Pour les valeurs à virgule flottante 32 bits, ce champ est de 8 bits. 1 représente le plus petit exposant et “tous les un – 1” le plus grand. (0 et “tous les uns” servent à coder des valeurs spéciales, voir ci-dessous.) Une valeur au milieu (127, dans le cas 32 bits) représente zéro, elle est également appelée polarisation .
  • En regardant la mantisse (la valeur entre 1.0 et (presque) 2.0), on voit que toutes les valeurs possibles commencent par un “1” (à la fois dans la représentation décimale et binary). Cela signifie qu’il ne sert à rien de le stocker. Le rest des chiffres binarys est stocké dans un champ entier, dans le cas 32 bits, ce champ est 23 bits.

Outre les valeurs à virgule flottante normales, il existe un certain nombre de valeurs spéciales:

  • Zéro est codé avec exposant et mantisse à zéro. Le bit de signe est utilisé pour représenter “plus zéro” et “moins zéro”. Un moins zéro est utile lorsque le résultat d’une opération est extrêmement faible, mais il est toujours important de savoir d’où vient l’opération.
  • plus et moins l’infini – représenté à l’aide d’un exposant “tous les un” et d’un champ de mantisse nul.
  • Pas un nombre (NaN) – représenté à l’aide d’un exposant “tous les un” et d’une mantisse non nulle.
  • Nombres dénormalisés – nombres plus petits que le plus petit nombre normal. Représenté à l’aide d’un champ à exposant nul et d’une mantisse non nulle. La particularité de ces nombres est que la précision (c.-à-d. Le nombre de chiffres qu’une valeur peut contenir) diminuera à mesure que la valeur diminue, tout simplement parce qu’il n’y a pas de place pour eux dans la mantisse.

Enfin, voici quelques exemples concrets (toutes les valeurs sont en hex):

  • 1,0: 3f800000
  • -1234.0: c49a4000
  • 100000000000000000000000000.0: 65a96816

En termes simples, c’est essentiellement une notation scientifique en binary. La norme officielle (avec les détails) est IEEE 754 .

  typedef struct { unsigned int mantissa_low:32; unsigned int mantissa_high:20; unsigned int exponent:11; unsigned int sign:1; } tDoubleStruct; double a = 1.2; tDoubleStruct* b = reinterpret_cast(&a); 

Voici un exemple de configuration de la mémoire si le compilateur utilise la double précision IEEE 754, qui est la valeur par défaut pour un double C sur la plupart des systèmes actuels.

Ici, il est sous forme binary basée sur C et il vaut mieux lire wikipedia sur la double précision pour le comprendre.

Il existe différents formats en virgule flottante. La plupart d’entre eux partagent quelques caractéristiques communes: un bit de signe, des bits dédiés au stockage d’un exposant et des bits dédiés au stockage du significande (également appelé mantisse).

La norme à virgule flottante IEEE tente de définir un format unique (ou plutôt un ensemble de formats de quelques tailles) pouvant être implémenté sur divers systèmes. Il définit également les opérations disponibles et leur sémantique. Cela fonctionne très bien et la plupart des systèmes que vous rencontrerez probablement utilisent une virgule flottante IEEE. Mais d’autres formats sont toujours utilisés, ainsi que des implémentations IEEE pas assez complètes. La norme C fournit une prise en charge optionnelle pour IEEE, mais ne l’impose pas.

La mantisse représente les bits les plus significatifs du nombre.

L’exposant représente le nombre de changements à effectuer sur la mantisse afin d’obtenir la valeur réelle du nombre.

Le codage spécifie comment sont représentés le signe de la mantisse et le signe de l’exposant (qu’il s’agisse essentiellement d’un décalage vers la gauche ou vers la droite).

Le document auquel vous faites référence spécifie le codage IEEE, le plus utilisé.

J’ai trouvé l’article que vous avez cité assez illisible (et je connais un peu le fonctionnement des flotteurs IEEE). Je vous suggère d’essayer avec la version Wiki de l’explication. C’est assez clair et a plusieurs exemples:

http://en.wikipedia.org/wiki/Single_precision et http://en.wikipedia.org/wiki/Double_precision

C’est la mise en œuvre définie, bien que l’IEEE-754 soit de loin le plus courant.

Pour être sûr que IEEE-754 est utilisé:

  • en C, utilisez #ifdef __STDC_IEC_559__
  • en C ++, utilisez les constantes std::numeric_limits::is_iec559

J’ai aimé l’explication donnée dans le lien ci-dessous. De plus, la section “Sujets que vous aimerez” pointe vers des réponses à d’autres requêtes similaires.

Partage pour les enregistrements au cas où un débutant cherche l’information.

https://www.log2base2.com/storage/how-float-values-are-stored-in-memory.html