16.1. Représentation machine des nombres décimaux#
16.1.1. Le problème#
Effectuer le calcul suivant et indiquer le problème
0.2 * 2.3
0.45999999999999996
La raison ce cet état de fait tient essentiellement aux puissances décroissantes de 2 et que certains nombres n’ont pas de représentation finie dans cette décomposition [1]
Exercise 16.1
En utilisant les puissances de deux d’exposant négatifs, donner un encadrement de 0,2.
En fait, l’imprécision est bien trop grande avec cette méthode. La norme IEEE 754 permet de représenter les floatants.
Note
Dans ce document, nous ne faisons pas une présentation complète de la norme, mais nous l’illustrons sur quelques exemples simples[2].
Fig. 16.1 Représentation d’un floatant en «demi-précision» (16 bits)#
Le bit de poids fort, noté \(f\) ici, correspond au signe du nombre. Les 5 nombres suivants (en demi-précision, soit 16 bits) correspond à l’exposant biaisé. On normalise celui-ci en soustrayant \(2^{e-1} - 1\) soit ici 15 car \(e = 5\).
Note
Par exemple, l’exposant biaisé \((11001)_2 = 25\) est normalisé en \(25 - 15 = 10\).
Pour finir, la mantisse est la partie décimale d’un nombre, avec la convention suivante :
si l’exposant est 0 ou \(2^e - 1\), alors la mantisse \(m\) est la partie décimale d’un nombre dont la partie entière est 0 (donc un nombre entre 0 et 1)
sinon, alors la mantisse \(m\) est la partie décimale d’un nombre dont la partie entière est 1 (donc un nombre compris entre 1 et 2)
Note
On a des exceptions :
0 : exposant = 0 et \(m = 0\)
infinis : exposant = \(2^e - 1\) et \(m = 0\)
Not a Number (NaN) : \(2^e - 1\) et \(m ≠ 0\)
On peut voir ça sur quelques exemples, avec des fonctions adhoc.
float_to_ieee754(0.25)
'0011010000000000'
ieee754_to_float('0011010000000000')
0.25
Attention, pour les grands nombres, la différence entre deux nombres consécutifs peut-être assez grande.
ieee754_to_float('0111101111111111')
65504.0
ieee754_to_float('0111101111111110')
65472.0
Attention, si on représente un nombre avec cette norme là et qu’on revient en arrière, on ne trouve pas toujours le nombre de départ.
float_to_ieee754(12.1)
'0100101000001101'
ieee754_to_float('0100101000001101')
12.1015625
Avertissement
On remarque qu’on ne peut pas représenter tous les nombres.
16.1.2. Une fonction de comparaison à mettre en œuvre#
Pour contourner le caractère discret du calcul des floatants, il est important de prendre l’habitude de ne pas faire de test d’égalité entre des floatants, mais de définir une fonction adhoc qui vérifie si les valeurs sont proches.
Note
On aurait pu utiliser sys.float_info.epsilon
import sys
sys.float_info.epsilon
2.220446049250313e-16
def est_egal(a: float, b: float) -> bool:
PRECISION = 10**(-10)
return abs(a - b) <= PRECISION
est_egal(10,10)
True
est_egal(0.2*2.3, 0.46)
True
est_egal(0.2*2.3,0.45)
False