Précision Matlab: la soustraction simple n'est pas nulle

Je calcule cette somme simple sur Matlab:

2*0.04-0.5*0.4^2 = -1.387778780781446e-017

mais le résultat n'est pas nul. Que puis-je faire?

1
ajouté l'auteur Amro, source
aussi un exemple beaucoup plus évident: 0.3 - 0.1 * 3 qui donne 5.5511e-017 .
ajouté l'auteur Amro, source
Lire sur eps .
ajouté l'auteur Mike DeSimone, source
5.498 + 0.001 ans = 5.499000000000001 la réponse correcte doit être 5.499
ajouté l'auteur James Do, source

5 Réponses

Aabaz et Jim Clay ont de bonnes explications sur ce qui se passe.

It's often the case that, rather than exactly calculating the value of 2*0.04 - 0.5*0.4^2, what you really want is to check whether 2*0.04 and 0.5*0.4^2 differ by an amount that is small enough to be within the relevant numerical precision. If that's the case, than rather than checking whether 2*0.04 - 0.5*0.4^2 == 0, you can check whether abs(2*0.04 - 0.5*0.4^2) < thresh. Here thresh can either be some arbitrary smallish number, or an expression involving eps, which gives the precision of the numerical type you're working with.

MODIFIER: Merci à Jim et Tal pour les améliorations suggérées. Modifié pour comparer la valeur absolue de la différence à un seuil plutôt qu'à la différence.

4
ajouté
Bon point. Le seul changement que je ferais est que vous devez comparer la valeur absolue de la différence à "thresh".
ajouté l'auteur Jim Clay, source

Matlab utilise des nombres à virgule flottante double précision pour stocker des nombres réels. Ce sont des nombres de la forme m * 2 ^ em est un entier compris entre 2 ^ 52 et 2 ^ 53 (la mantisse ) et e est l’exposant. Appelons un nombre un nombre à virgule flottante s'il est de cette forme.

Tous les nombres utilisés dans les calculs doivent être des nombres à virgule flottante. Cela peut souvent être fait exactement, comme avec 2 et 0.5 dans votre expression. Mais pour les autres nombres, notamment la plupart des nombres avec des chiffres après le signe décimal, cela n’est pas possible et une approximation doit être utilisée. Dans ce cas, le nombre est arrondi au nombre à virgule flottante le plus proche.

So, whenever you write something like 0.04 in Matlab, you're really saying "Get me the floating-point number that is closest to 0.04. In your expression, there are 2 numbers that need to be approximated: 0.04 and 0.4.

De plus, le résultat exact d'opérations telles que l'addition et la multiplication sur des nombres à virgule flottante peut ne pas être un nombre à virgule flottante. Bien qu’elle soit toujours de la forme m * 2 ^ e , la mantisse est peut-être trop grande. Vous obtenez donc une erreur supplémentaire en arrondissant les résultats des opérations.

À la fin de la journée, une expression simple comme la vôtre sera décalée d’environ 2 à 52 fois la taille des opérandes, soit environ 10 ^ à 17.

En résumé: la raison pour laquelle votre expression n'est pas évaluée à zéro est double:

  1. Certains des chiffres avec lesquels vous commencez sont différents (approximations) des chiffres exacts que vous avez fournis.
  2. Les résultats intermédiaires peuvent également être des approximations des résultats exacts.
2
ajouté

Je ne sais pas si cela s'applique à votre problème, mais la solution la plus simple consiste souvent à faire évoluer vos données.

Par exemple:

a=0.04;
b=0.2;
a-0.2*b
ans=-6.9389e-018
c=a/min(abs([a b]));
d=b/min(abs([a b]));
c-0.2*d
ans=0

EDIT: of course I did not mean to give a universal solution to these kind of problems but it is still a good practice that can make you avoid a few problems in numerical computation (curve fitting, etc ...). See Jim Clay's answer for the reason why you are experiencing these problems.

1
ajouté
Y a-t-il une raison pour que cela fonctionne, ou est-ce juste un code ad-hoc qui fait la bonne chose dans ce cas?
ajouté l'auteur Oliver Charlesworth, source
Honnêtement, je ne le sais pas, mais cela va certainement dans la bonne direction pour résoudre ce genre de problèmes.
ajouté l'auteur Aabaz, source
Est-ce que cela fonctionne toujours ou juste de temps en temps?
ajouté l'auteur Jim Clay, source
Cela ne fonctionnera pas toujours comme on peut le constater en essayant f = @ (x) 1-x/x ^ 2 * x puis f (rand ()) fois. Parfois, c'est 0; d'autres fois c'est epsilon. La mise à l'échelle vous aide à obtenir de meilleurs résultats, mais la précision est toujours dans epsilon. Une autre solution consiste à adapter epsilon à l’échelle des autres valeurs du problème.
ajouté l'auteur stardt, source

Ce que vous voyez est une erreur de quantification . Matlab utilise des doubles pour représenter des nombres, et bien qu'ils soient capables de beaucoup de précision, ils ne peuvent toujours pas représenter tous les nombres réels car il existe un nombre infini de nombres réels. Je ne suis pas sûr du truc d'Aabaz, mais en général, je dirais que vous ne pouvez rien faire, à part peut-être transformer vos entrées en chiffres à deux amis.

1
ajouté

Je suis à peu près sûr qu'il s'agit de problèmes de précision en virgule flottante.

Avez-vous besoin de précision 1e-17? S'agit-il simplement de vouloir une "jolie" sortie? Dans ce cas, vous pouvez simplement utiliser un sprintf formaté pour afficher le nombre de chiffres significatifs de votre choix.

Sachez que ce n'est pas un problème matlab, mais une limitation fondamentale de la façon dont les nombres sont représentés en binaire.

Pour le plaisir, déterminez ce que .1 est en binaire ...

Quelques références: http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems http://www.mathworks.com/support/tech-notes/1100/1108.html

1
ajouté