Le but de cette page n'est pas d'expliquer en profondeur le format IEEE mais de comprendre "l'erreur" qui se produit dans le source suivant :
Dim sng As Single 'Nombre réel simple précision Dim dbl As Double 'Nombre réel double précision Private Sub Form_Load() 'on affecte 1.9 a la variable sng sng = 1.9 'on affecte la valeur de sng a dbl dbl = sng 'on Affiche dbl MsgBox dbl End Sub
D'après vous quel nombre va apparaitre ?
1.9 ?
Si vous avez VB faites donc le test (il faut le voir pour le croire) avant de lire la suite plus bas
Voici ce qui apparait a l'écran :
Incroyable non ?
Les sceptiques pourront ajouter une fonction de convertion :
dbl = CDbl(sng)
Cela ne change rien l'erreur est toujours là !
Cette erreur est dans cet exemple tres visible, elle pourrait tout aussi bien se cacher lors d'une requête dans une base de donnée
Pour commencer plongeons nous dans MSDN :
Les variables de type Double (à virgule flottante en double précision) sont
stockées sous la forme de nombres à virgule flottante de 64 bits (8 octets)
IEEE dont la valeur est comprise entre -1,79769313486232E308 et -4,94065645841247E-324
pour les nombres négatifs et entre 4,94065645841247E-324 et 1,79769313486232E308
pour les positifs.
Le caractère de déclaration de type Double est le signe #.
Cela ne nous avance pas trop, on nous confirme que le double est plus précis que le simple mais c'est tout.
Pour comprendre il faut aller plus loin à l'interieur même du format de stockage des nombres réels sur PC: le format IEEE.
Les nombres entiers sont convertis en binaires pour etre stockés dans un ordinateur
Avec la calculatrice Windows (en affichage Scientifique) il est possible de
faire des convertions rapidement
5 s'écrit en binaire 101
c'est a dire :
5=1x2^2+0x2^1+1x2^0
5=1x4+0x2+1x1
5 =4+1
Pour les nombres entiers c'est très facile mais plus le nombre est grand
et plus il faut de bits pour l'écrire
c'est pour ça que les nombres entiers sont limités à une plage de valeurs
dans tous les ordinateurs.
Extrait de MSDN :
Les variables de type Long (entier long) sont stockées sous la forme de nombres
signés de 32 bits (4 octets)
dont la valeur est comprise entre -2 147 483 648 et 2 147 483 647.
Pour aller plus loin sans utiliser trop de mémoire on utilise des nombres réels simple et double
Pour stocker un nombre à "nombres à virgule flottante"
x en binaire la maniere simple serait de codé deux entiers a et b
en écrivant x=a * 10^b
exemple 1.9=19* 10^-1
avec cette écriture pas d'erreur possible en simple ou en double précision.
En notation IEEE x n'est pas écrit a * 10^b
mais x = -1 ^ S x 2 ^ E x 1. F
S E et F etant trois nombres entiers
( F représente les decimales de 1.F )
( S vaut 0 ou 1 )
( E peut être négatif )
Le calcul pour obtenir S,E et F n'est pas détaillé
ici (voir le programme)
Exemple:
6,5 n'est pas écrit 65 x 10^-1
Mais
-1^0 x 2^2 x 1.625
Le nombre stocké est donc SEF
La taille en bit de S, E et F et variable :
Pour les simples:
S : 1 bit
E : 8 bits
F : 23 bits
Au total 1+8+23=32 bits
On retrouve bien les 4 octets de précision
Pour les doubles:
S : 1 bit
E : 11 bits
F : 52 bits
Au total 1+11+52=64 bits
On retrouve les 8 octets de précison
Cette notation ne permet pas d'écrire précisement
tout les nombres
F etant limité on obtient souvent une approximation de x !
L'ordinateur doit calculer cette approximation dès que l'on affecter une valeur dans un single ou dans un double
L'ordinateur doit recalculer le nombre (a chaque fois que l'on
y accède) à partir de la notation stokée,
le calcul est plus précis lorsque le nombre se trouve dans un double.
Mais lorsque l'on affecte un single dans un double le calcul ne
se refait pas, la notation single et placée directement dans le double
L'approximation etant moins bonne (et surtout inférieur a la précisions
du double) l'erreur devient visible lorsque l'on recalcule la valeur
Exemple (cf les sources du programme joint)
Le nombre 1.9 est stocké :
dans un SINGLE sous la forme binaire : 0 01111111 11100110011001100110011
dans un DOUBLE sous la forme binaire : 0 00001111111 1110011001100110011001100110011001100110011001100110
dans un DOUBLE provenant d'un SINGLE : 0 00001111111 11100110011001100110011 00000000000000000000000000000
la difference est visible à la fin, le recalcul de la valeur du dernier DOUBLE ne donnera pas 1.9 mais 1,89999997615814
pour éviter cette erreur on peut passer par un STRING intermédiare
cela impose le recalcul de la notation dans le double.
(Voir la fonction Cdouble du programme d'exemple)