Arithmétique à virgule flottante

Bienvenue dans un autre épisode de Under The Hood . Cette colonne vise à donner aux développeurs Java un aperçu de la beauté cachée sous leurs programmes Java en cours d'exécution. La chronique de ce mois poursuit la discussion, commencée le mois dernier, du jeu d'instructions bytecode de la machine virtuelle Java (JVM). Cet article examine l'arithmétique en virgule flottante dans la JVM et couvre les bytecodes qui effectuent des opérations arithmétiques en virgule flottante. Les articles suivants traiteront d'autres membres de la famille de bytecode.

Les principaux points flottants

La prise en charge en virgule flottante de la JVM est conforme à la norme de virgule flottante IEEE-754 1985. Cette norme définit le format des nombres à virgule flottante 32 bits et 64 bits et définit les opérations sur ces nombres. Dans la JVM, l'arithmétique en virgule flottante est effectuée sur des flottants 32 bits et des doubles 64 bits. Pour chaque bytecode qui effectue l'arithmétique sur les flottants, il existe un bytecode correspondant qui effectue la même opération sur les doubles.

Un nombre à virgule flottante comprend quatre parties: un signe, une mantisse, une base et un exposant. Le signe est un 1 ou -1. La mantisse, toujours un nombre positif, contient les chiffres significatifs du nombre à virgule flottante. L'exposant indique la puissance positive ou négative de la base par laquelle la mantisse et le signe doivent être multipliés. Les quatre composants sont combinés comme suit pour obtenir la valeur en virgule flottante:

signe * mantisse * exposant de base

Les nombres à virgule flottante ont plusieurs représentations, car on peut toujours multiplier la mantisse de tout nombre à virgule flottante par une puissance de la base et changer l'exposant pour obtenir le nombre d'origine. Par exemple, le nombre -5 peut être représenté de manière égale par l'une des formes suivantes dans la base 10:

Formes de -5
Signe Mantisse Exposant Radix
-1 50 10 -1
-1 5 10 0
-1 0,5 10 1
-1 0,05 10 2

Pour chaque nombre à virgule flottante, il y a une représentation qui est dite normalisée. Un nombre à virgule flottante est normalisé si sa mantisse est dans la plage définie par la relation suivante:

1 / radix <= mantisse <

Un nombre à virgule flottante de base normalisée 10 a son point décimal juste à gauche du premier chiffre différent de zéro dans la mantisse. La représentation normalisée en virgule flottante de -5 est -1 * 0,5 * 10 1. En d'autres termes, la mantisse d'un nombre à virgule flottante normalisée n'a pas de chiffres non nuls à gauche de la virgule décimale et un chiffre non nul juste pour la droite du point décimal. Tout nombre à virgule flottante qui ne rentre pas dans cette catégorie est dit dénormalisé . Notez que le nombre zéro n'a pas de représentation normalisée, car il n'a pas de chiffre différent de zéro à placer juste à droite du point décimal. "Pourquoi être normalisé?" est une exclamation courante parmi les zéros.

Les nombres à virgule flottante dans la JVM utilisent une base de deux. Les nombres à virgule flottante dans la JVM ont donc la forme suivante:

signe * mantisse * 2 exposant

La mantisse d'un nombre à virgule flottante dans la JVM est exprimée sous forme de nombre binaire. Une mantisse normalisée a son point binaire (l'équivalent en base deux d'un point décimal) juste à gauche du chiffre non nul le plus significatif. Parce que le système de nombres binaires n'a que deux chiffres - zéro et un - le chiffre le plus significatif d'une mantisse normalisée est toujours un.

Le bit le plus significatif d'un flottant ou d'un double est son bit de signe. La mantisse occupe les 23 bits les moins significatifs d'un flottant et les 52 bits les moins significatifs d'un double. L'exposant, 8 bits dans un flottant et 11 bits dans un double, se situe entre le signe et la mantisse. Le format d'un flottant est indiqué ci-dessous. Le bit de signe est représenté par un "s", les bits d'exposant sont représentés par "e" et les bits de mantisse sont représentés par "m":

Disposition des bits du flotteur Java
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm

Un bit de signe de zéro indique un nombre positif et un bit de signe de un indique un nombre négatif. La mantisse est toujours interprétée comme un nombre positif en base deux. Ce n'est pas un nombre à complément de deux. Si le bit de signe est un, la valeur en virgule flottante est négative, mais la mantisse est toujours interprétée comme un nombre positif qui doit être multiplié par -1.

Le champ d'exposant est interprété de trois manières. Un exposant de tous les uns indique que le nombre à virgule flottante a l'une des valeurs spéciales de plus ou moins l'infini, ou "pas un nombre" (NaN). NaN est le résultat de certaines opérations, comme la division de zéro par zéro. Un exposant de tous les zéros indique un nombre à virgule flottante dénormalisé. Tout autre exposant indique un nombre à virgule flottante normalisé.

La mantisse contient un peu de précision supplémentaire au-delà de ceux qui apparaissent dans les bits de mantisse. La mantisse d'un flottant, qui n'occupe que 23 bits, a 24 bits de précision. La mantisse d'un double, qui occupe 52 bits, a 53 bits de précision. Le bit de mantisse le plus significatif est prévisible et n'est donc pas inclus, car l'exposant des nombres à virgule flottante dans la JVM indique si le nombre est normalisé ou non. Si l'exposant est entièrement zéros, le nombre à virgule flottante est dénormalisé et le bit le plus significatif de la mantisse est connu pour être un zéro. Sinon, le nombre à virgule flottante est normalisé et le bit le plus significatif de la mantisse est connu pour être un.

La machine virtuelle Java ne génère aucune exception à la suite d'opérations en virgule flottante. Des valeurs spéciales, telles que l'infini positif et négatif ou NaN, sont renvoyées à la suite d'opérations suspectes telles que la division par zéro. Un exposant de tous les uns indique une valeur flottante spéciale. Un exposant de tous les uns avec une mantisse dont les bits sont tous à zéro indique une infinité. Le signe de l'infini est indiqué par le bit de signe. Un exposant de tous les uns avec une autre mantisse est interprété comme signifiant «pas un nombre» (NaN). La JVM produit toujours la même mantisse pour NaN, qui est tous des zéros à l'exception du bit de mantisse le plus significatif qui apparaît dans le nombre. Ces valeurs sont affichées pour un flottant ci-dessous:

Valeurs flottantes spéciales
Valeur Bits flottants (mantisse exposant de signe)
+ Infini 0 11111111 00000000000000000000000
-Infini 1 11111111 00000000000000000000000
NaN 1 11111111 10000000000000000000000

Les exposants qui ne sont ni tous des uns ni tous des zéros indiquent la puissance de deux par laquelle multiplier la mantisse normalisée. La puissance de deux peut être déterminée en interprétant les bits d'exposant comme un nombre positif, puis en soustrayant un biais du nombre positif. Pour un float, le biais est de 126. Pour un double, le biais est de 1023. Par exemple, un champ d'exposant dans un float de 00000001 donne une puissance de deux en soustrayant le biais (126) du champ d'exposant interprété comme un entier positif (1). La puissance de deux est donc de 1 à 126, soit -125. C'est la plus petite puissance possible de deux pour un flotteur. À l'autre extrême, un champ d'exposant de 11111110 donne une puissance de deux de (254 - 126) ou 128. Le nombre 128 est la plus grande puissance de deux disponible pour un flotteur. Plusieurs exemples de flottants normalisés sont présentés dans le tableau suivant:

Valeurs flottantes normalisées
Valeur Bits flottants (mantisse exposant de signe) Exposant impartial
Le plus grand flottant positif (fini) 0 11111110 11111111111111111111111 128
Le plus grand flottant négatif (fini) 1 11111110 11111111111111111111111 128
Le plus petit flotteur normalisé 1 00000001 00000000000000000000000 -125
Pi 0 10000000 10010010000111111011011 2

Un exposant de tous les zéros indique que la mantisse est dénormalisée, ce qui signifie que le bit de tête non déclaré est un zéro au lieu d'un un. La puissance de deux dans ce cas est la même que la puissance la plus faible de deux disponible pour une mantisse normalisée. Pour le flotteur, c'est -125. Cela signifie que les mantisses normalisées multipliées par deux élevées à la puissance de -125 ont un champ d'exposant de 00000001, tandis que les mantisses dénormalisées multipliées par deux élevées à la puissance de -125 ont un champ d'exposant de 00000000. La tolérance pour les nombres dénormalisés en bas la fin de la plage d'exposants prend en charge le dépassement progressif. Si l'exposant le plus bas était plutôt utilisé pour représenter un nombre normalisé, un dépassement inférieur à zéro se produirait pour des nombres plus grands. En d'autres termes, laisser l'exposant le plus bas pour les nombres dénormalisés permet de représenter des nombres plus petits.Les nombres dénormalisés plus petits ont moins de bits de précision que les nombres normalisés, mais cela est préférable à un dépassement à zéro dès que l'exposant atteint sa valeur normalisée minimale.

Valeurs flottantes dénormalisées
Valeur Bits flottants (mantisse exposant de signe)
Plus petit flotteur positif (non nul) 0 00000000 00000000000000000000001
Plus petit flottant négatif (non nul) 1 00000000 00000000000000000000001
Le plus grand flotteur dénormalisé 1 00000000 11111111111111111111111
Zéro positif 0 00000000 00000000000000000000000
Zéro négatif 1 00000000 00000000000000000000000

Flotteur exposé

Un flotteur Java révèle sa nature interne L'applet ci-dessous vous permet de jouer avec le format à virgule flottante. La valeur d'un float est affichée dans plusieurs formats. Le format de notation scientifique radix deux montre la mantisse et l'exposant en base dix. Avant d'être affichée, la mantisse réelle est multipliée par 2 24, ce qui donne un nombre entier, et l'exposant non biaisé est décrémenté de 24. La mantisse intégrale et l'exposant sont ensuite facilement convertis en base dix et affichés.