Corriger les erreurs dans les résultats des opérations en virgule flottante
par
, 29/01/2021 à 12h09 (1731 Affichages)
Objectif : corriger les erreurs dans les résultats des opérations portant sur des nombres à virgule flottante, floating-point error en anglais.
En VBA le type Double par exemple, contient des nombres à virgule flottante double précision IEEE 64 bits (8 octets) compris entre-1.79769313486231570 E+308 et -4.94065645841246544 E-324 pour les valeurs négatives et de 4.94065645841246544 E-324 à 1.79769313486231570 E+308 pour les valeurs positives.
Les nombres à double précision stockent une approximation d’un nombre réel. Ceci explique pourquoi il peut y avoir de légères imprécisions quand on effectue des opérations sur ces nombres.
Par exemple, si vous souhaitez représenter Pi ou 1/3 sous une forme décimale, il est évident que vous n'obtiendrez jamais une valeur exacte.
Au cours du développement, si vous n'avez pas la possibilité d'utiliser un type décimal ou monétaire à la place, vous pouvez avoir à corriger des erreurs dans les résultats des opérations portant sur des nombres à virgule flottante.
I. Correction des erreurs sur les résultats d'opérations
Prenez par exemple une simple soustraction entre 2 nombres :
12.1 - 12 donne 0,0999999999999996 au lieu de 0.1
Dans ce cas, il faut utiliser la fonction Round(nombre, nombre_décimales), pour arrondir le résultat avec une certaine précision et ainsi corriger l'erreur :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part ? Round(12.1 - 12, 5)
va bien donner 0.1
choisir pour l'arrondi un nombre de décimales suffisamment grand pour ne pas arrondir de trop le nombre.
Dans les formulaires Access, ces erreurs peuvent aussi être corrigées sur les sommes de valeurs affichées dans des zones de texte :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part = Round(somme([champ_valeur]);5)
Ou dans une requête :
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 select Round(Sum([colonne_valeur]),5) as total from table_valeurs;
Ou encore dans une cellule d'une feuille Excel, C1 étant la cellule contenant le résultat à corriger :
Code : Sélectionner tout - Visualiser dans une fenêtre à part =ARRONDI(C1;5)
II. Correction des erreurs de comparaison entre valeurs numériques
Ces problèmes peuvent aussi se rencontrer quand on souhaite effectuer une comparaison entre valeurs dans le code :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 if somme_valeurs >= 2000# then ' teste si la somme est supérieure ou égale à un certain seuil de 2000 ' traitement si condition respectée end if
Imaginez que la valeur de la variable somme_valeurs soit le résultat d'une somme de nombres à virgule flottante, il se peut que le total donne par exemple 1999.9999999999999996, au lieu de 2000, et dans ce cas la condition ne serait pas respectée alors qu'elle devrait l'être.
Pour corriger l'erreur dans la somme et faire en sorte que la condition soit respectée, vous pouvez à nouveau utiliser la fonction Round :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 if Round(somme_valeurs,5) >= 2000# then ' teste si la somme est supérieure ou égale à un certain seuil de 2000 ' traitement si condition respectée end if
Si vous souhaitez maintenant simplement vérifier si la somme est égal à 2000 :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 if somme_valeurs = 2000# then ' teste si la somme est égale à un certain seuil de 2000 ' traitement si condition respectée end if
Vous pouvez également contrôler si l'écart en valeur absolue entre les 2 nombres ne dépasse pas une certaine marge d'erreur :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 if Abs(somme_valeurs - 2000#) <= 0.000001 then ' teste si l'écart en valeur absolue entre les 2 nombres ne dépasse pas une marge d'erreur de 0.000001 ' traitement si condition respectée end if
III. Correction des erreurs de comparaison entre dates
Ces erreurs peuvent aussi se produire quand on effectue des opérations sur des dates qui sont en fait stockées sous forme de nombres à virgule flottante. La partie entière représente la date et la partie décimale l'heure.
Prenons deux variables de type Date, dt1 et dt2, initialisées au 29/01/2021 14:00 soit en VBA au #1/29/2021 2:00:00 PM# :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 Dim dt1 as date, dt2 as date ' initialisation des variables dt1 = #1/29/2021 2:00:00 PM# dt2 = #1/29/2021 2:00:00 PM#
Ajoutons deux fois 15 minutes à la variable dt1, et directement 30 minutes à la variable dt2 :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 dt1 = DateAdd("n", 15, dt1) ' ajoutons 15 minutes à la date de départ du 29/01/2021 à 14:00 dt1 = DateAdd("n", 15, dt1) ' ajoutons encore 15 minutes à la date obtenue dt2 = DateAdd("n", 30, dt2) ' ajoutons directement 30 minutes à la date de départ du 29/01/2021 à 14:00
Comparons maintenant les variables qui devraient logiquement contenir toutes deux la valeur #1/29/2021 2:30:00 PM# :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 If dt1 = dt2 Then ' comparons les 2 dates obtenues Debug.Print "les dates sont égales !" Else Debug.Print "les dates ne sont pas égales !" End If
L'exécution du code complet affiche pourtant "Les dates ne sont pas égales !"
On est a nouveau en présence d'erreurs de précision dans les résultats d'opérations portant sur des nombres à virgule flottante, les valeurs numériques des deux dates obtenues étant légèrement différentes.
La solution passe une nouvelle fois par l'utilisation de la fonction Round pour comparer les dates :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 If Round(dt1,5) = Round(dt2,5) Then ' comparons les 2 dates obtenues Debug.Print "les dates sont égales !" Else Debug.Print "les dates ne sont pas égales !" End If
Après cette modification, l'exécution du code donne bien "Les dates sont égales !"
Vous pouvez bien sûr également utiliser la fonction format pour les comparaisons avec if format(dt1,"dd/mm/yyyy hh:nn:ss") = format(dt2,"dd/mm/yyyy hh:nn:ss") then.
Voilà, j'espère que ça en aidera certains, car j'ai moi-même été pas mal dérouté au début pas ces problèmes avec les nombres à virgule flottante : on obtient un résultat inattendu alors qu'on a tout bien fait