Nombre dénormalisé

Un article de Wikipédia, l'encyclopédie libre.

La notion de nombre dénormalisé intervient dans la représentation des nombres en informatique par la méthode de la virgule flottante, telle que normalisé par la norme IEEE 754. C'est une manière de représenter des nombres ayant une valeur très proche de zéro.

Prérequis[modifier | modifier le code]

La représentation en virgule flottante pour les ordinateurs est fondée sur deux notions : la notation scientifique et le système binaire (ou base 2).

Pour écrire les nombres en base 10, on peut utiliser la notation dite « scientifique », qui comprend deux parties :

  • un nombre décimal n, positif ou négatif, dont la valeur absolue est comprise entre 1 inclus et 10 exclus ; 1 ≤ n < 10 ;
  • une puissance entière de 10, a ;

sous la forme « n × 10a ». Par exemple :

  • 1 en écriture scientifique se note « 1 × 100 » ;
  • -85 en écriture scientifique se note « -8,5 × 101 » ;
  • 0,012 3 en écriture scientifique se note « 1,23 × 10−2 ».

Le terme « exposant » correspond à la puissance de 10, et le terme mantisse correspond à la partie décimale. Ainsi, dans « 1,23 × 10−2 »,

  • la mantisse (ou significande) est « 1,23 » ;
  • l'exposant est « -2 ».

Le système binaire, dit aussi « base 2 », est une manière de noter les nombres ne faisant intervenir que deux chiffres, 0 et 1. Il est bien adapté à l'électronique, puisque cela correspond à deux états bien distincts : on a aux bornes d'un composant une tension V0 ou une tension V1 (« le courant ne passe pas ou le courant passe »). Pour distinguer cette notation de la notation décimale, nous ajoutons ici un « b » à la fin du nombre. Voici quelques exemples de nombres :

  • 0b = 0
  • 1b = 1 ;
  • 10b = 2 ;
  • 11b = 3 ;
  • 0,1b = 0,5 ;
  • 0,01b = 0,25 ;
  • 0,11b = 0,75 ;

Un nombre binaire à virgule de quatre chiffres n1n0,n-1n-2b correspond au nombre décimal n1 × 21 + n0 × 20 + n-1 × 2-1 + n-2 × 2-2.

On peut ainsi avoir une notation scientifique binaire :

n1n0,n-1n-2b peut se noter n1,n0n-1n-2b × 21.

par exemple

  • 11b = 1,1b × 21
  • 0,11b = 1,1b × 2-1

Dans le cas de la notation scientifique binaire, le nombre à virgule doit être compris entre 1b inclus et 10b exclus (c'est-à-dire 2 exclus), c'est-à-dire que sa partie entière est nécessairement 1b.

Représentation d'un nombre normalisé[modifier | modifier le code]

Selon ladite norme, la représentation d'un nombre réel peut se décomposer en trois parties :

  • le signe, +1 ou -1, sous la forme de un bit ;
  • l'exposant décalé, sous la forme de e bits représentant un nombre entier ;
  • la mantisse, sous la forme de m bits représentant un nombre positif strictement inférieur à 1.

La valeur du nombre représenté vaut :

valeur = signe × (1 + mantisse) × 2(exposant − décalage)

En effet, en notation scientifique en base binaire, la partie entière est nécessairement 1, il est donc inutile d'utiliser un bit pour la représenter, on se contente de représenter la partie fractionnaire.

Représentation d'un nombre dénormalisé[modifier | modifier le code]

Un nombre est dit dénormalisé lorsque

  • exposant = 0, et
  • mantisse ≠ 0.

Si l'on suivait la même représentation que pour les nombres à exposant non nul, on aurait

valeur = signe × (1 + mantisse) × 2-décalage

mais la valeur retenue est

valeur = signe × mantisse × 2-décalage + 1

c'est-à-dire que pour un exposant codé sur 8 bits (représentation dite à simple précision), on a

valeur = signe × mantisse × 2-126

et que pour un exposant codé sur 11 bits (représentation dite à double précision), on a

valeur = signe × mantisse × 2-1022

Raison de cette représentation[modifier | modifier le code]

Avec les nombres dénormalisés, on abandonne la notation scientifique. Si la mantisse correspond à 0,1b (le « b » signifie que l'on est en mode binaire, en base deux), alors :

  • avec la représentation normalisée, on aurait
    valeur = 1,1b × 2-décalage ;
  • avec la représentation dénormalisée, on a
    valeur = 0,1b × 2-décalage + 1 = 1b × 2-décalage

on voit ainsi que le plus petit nombre représentable de manière normalisée est 1,000…00b × 2-décalage (puisqu'une mantisse nulle et un exposant nul servent à représenter le zéro), alors qu'en représentation dénormalisée, c'est 0,000…01b × 2-décalage. On peut donc représenter des nombres plus petits. Cela assure également une continuité avec les nombres normalisés, puisque :

  • le plus petit nombre normalisé vaut 1,000…00b × 2-décalage+ 1
  • le plus grand nombre dé normalisé vaut 0,111…11b × 2-décalage + 1

soit un « saut » de 0,000…01b × 2-décalage + 1

Nous avons donc, en simple précision :

Type Exposant Mantisse Valeur approchée Écart / préc
Zéro 0000 0000 000 0000 0000 0000 0000 0000 0,0
Plus petit nombre dénormalisé 0000 0000 000 0000 0000 0000 0000 0001 1,4 × 10−45 1,4 × 10−45
Nombre dénormalisé suivant 0000 0000 000 0000 0000 0000 0000 0010 2,8 × 10−45 1,4 × 10−45
Nombre dénormalisé suivant 0000 0000 000 0000 0000 0000 0000 0011 4,2 × 10−45 1,4 × 10−45
Plus grand nombre dénormalisé 0000 0000 111 1111 1111 1111 1111 1111 1,175 494 21 × 10−38 1,4 × 10−45
Plus petit nombre normalisé 0000 0001 000 0000 0000 0000 0000 0000 1,175 494 35 × 10−38 1,4 × 10−45
Nombre normalisé suivant 0000 0001 000 0000 0000 0000 0000 0001 1,175 494 49 × 10−38 1,4 × 10−45

Impact sur les performances[modifier | modifier le code]

Certaines architectures réalisent des opérations sur les nombres dénormalisés au niveau matériel, cependant il est possible que ces opérations soient effectuées au niveau logiciel. Cela peut avoir pour conséquence de créer des ralentissements conséquents.

Il est possible de demander au processeur de traiter ces nombres au niveau matériel en les ramenant à 0 soit Flush-To-Zero (FTZ) et Denormals-Are-Zero (DAZ). Il faut pour cela activer les bons registres :

sur x86_64:

#if defined(_x86-64)
    asm("stmxcsr -0x4(%rsp)\n\t"          /* stock le registre CSR sur la pile */
        "orl     $0x8040, -0x4(%rsp)\n\t" /* modifie la valeur des  bits 15( FTZ ) and 7( DAZ ) */
        "ldmxcsr -0x4(%rsp)" );           /* charge le registre CSR depuis la pile */
#endif

sur Aarch64:

#if defined(__arm64__) || defined(__aarch64__)
    uint64_t fpcr;
    asm("mrs %0,   fpcr" : "=r"( fpcr ));             /* Charge le registre FPCR */
    asm("msr fpcr, %0"   :: "r"( fpcr | (1 << 24) )); /* Modifie la valeur du 24eme bit (FTZ) */
#endif

Voir aussi[modifier | modifier le code]

Liens externes[modifier | modifier le code]