Pourquoi 24.0000 n’est-il pas égal à 24.0000 dans MATLAB?

J’écris un programme où je dois supprimer les points en double stockés dans une masortingce. Le problème est que quand il s’agit de vérifier si ces points sont dans la masortingce, MATLAB ne peut pas les reconnaître dans la masortingce bien qu’ils existent.

Dans le code suivant, la fonction des intersections obtient les points d’intersection:

 [points(:,1), points(:,2)] = intersections(... obj.modifiedVGVertices(1,:), obj.modifiedVGVertices(2,:), ... [vertex1(1) vertex2(1)], [vertex1(2) vertex2(2)]); 

Le résultat:

 >> points points = 12.0000 15.0000 33.0000 24.0000 33.0000 24.0000 >> vertex1 vertex1 = 12 15 >> vertex2 vertex2 = 33 24 

Deux points ( vertex1 et vertex2 ) doivent être éliminés du résultat. Cela devrait être fait par les commandes ci-dessous:

 points = points((points(:,1) ~= vertex1(1)) | (points(:,2) ~= vertex1(2)), :); points = points((points(:,1) ~= vertex2(1)) | (points(:,2) ~= vertex2(2)), :); 

Après cela, nous avons ce résultat inattendu:

 >> points points = 33.0000 24.0000 

Le résultat devrait être une masortingce vide. Comme vous pouvez le voir, la première (ou seconde?) Paire de [33.0000 24.0000] a été éliminée, mais pas la seconde.

Puis j’ai vérifié ces deux expressions:

 >> points(1) ~= vertex2(1) ans = 0 >> points(2) ~= vertex2(2) ans = 1 % <-- It means 24.0000 is not equal to 24.0000? 

Quel est le problème?


Plus surprenant, j’ai fait un nouveau script qui ne contient que ces commandes:

 points = [12.0000 15.0000 33.0000 24.0000 33.0000 24.0000]; vertex1 = [12 ; 15]; vertex2 = [33 ; 24]; points = points((points(:,1) ~= vertex1(1)) | (points(:,2) ~= vertex1(2)), :); points = points((points(:,1) ~= vertex2(1)) | (points(:,2) ~= vertex2(2)), :); 

Le résultat attendu:

 >> points points = Empty masortingx: 0-by-2 

Le problème que vous rencontrez concerne la manière dont les nombres à virgule flottante sont représentés sur un ordinateur. Une discussion plus détaillée des représentations en virgule flottante apparaît vers la fin de ma réponse (section “Représentation en virgule flottante”). La version TL; DR : les ordinateurs ayant des quantités de mémoire limitées, les nombres ne peuvent être représentés qu’avec une précision finie. Ainsi, la précision des nombres à virgule flottante est limitée à un certain nombre de décimales (environ 16 chiffres significatifs pour les valeurs à double précision , la valeur par défaut utilisée dans MATLAB).

Précision réelle vs affichée

Maintenant, pour répondre à l’exemple spécifique de la question … alors que 24.0000 et 24.0000 sont affichés de la même manière, il s’avère qu’ils diffèrent réellement par de très petites quantités décimales dans ce cas. Vous ne le voyez pas car MATLAB n’affiche que 4 chiffres significatifs par défaut , ce qui permet de garder l’affichage global propre et net. Si vous voulez voir la précision complète, vous devez soit émettre la commande format long ou afficher une représentation hexadécimale du nombre:

 >> pi ans = 3.1416 >> format long >> pi ans = 3.141592653589793 >> num2hex(pi) ans = 400921fb54442d18 

Valeurs initialisées et valeurs calculées

Comme il n’y a qu’un nombre fini de valeurs pouvant être représenté pour un nombre à virgule flottante, il est possible qu’un calcul se traduise par une valeur comprise entre deux de ces représentations. Dans un tel cas, le résultat doit être arrondi à l’un d’eux. Cela introduit une petite erreur de précision de la machine . Cela signifie également que l’initialisation d’une valeur directement ou par un calcul peut donner des résultats légèrement différents. Par exemple, la valeur 0.1 n’a pas de représentation exacte en virgule flottante (c.-à-d. Qu’elle est légèrement arrondie), et vous obtenez donc des résultats contre-intuitifs comme celui-ci en raison de la façon dont les erreurs d’arrondi s’accumulent:

 >> a=sum([0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1]); % Sum 10 0.1s >> b=1; % Initialize to 1 >> a == b ans = logical 0 % They are unequal! >> num2hex(a) % Let's check their hex representation to confirm ans = 3fefffffffffffff >> num2hex(b) ans = 3ff0000000000000 

Comment gérer correctement les comparaisons en virgule flottante

Étant donné que les valeurs à virgule flottante peuvent différer de très petites quantités, toute comparaison doit être effectuée en vérifiant que les valeurs se situent dans une certaine fourchette (c.-à-d. Une tolérance) les unes par rapport aux autres, par opposition aux valeurs identiques. Par exemple:

 a = 24; b = 24.000001; tolerance = 0.001; if abs(ab) < tolerance, disp('Equal!'); end 

affichera "Equal!".

Vous pourriez alors changer votre code en quelque chose comme:

 points = points((abs(points(:,1)-vertex1(1)) > tolerance) | ... (abs(points(:,2)-vertex1(2)) > tolerance),:) 

Représentation en virgule flottante

Un bon aperçu des nombres à virgule flottante (et plus précisément de la norme IEEE 754 pour l'arithmétique à virgule flottante ) est ce que chaque informaticien devrait savoir sur l'arithmétique en virgule flottante de David Goldberg.

Un nombre à virgule flottante binary est en fait représenté par trois nombres entiers: un bit de signe s , un significande (ou coefficient / fraction) b et un exposant e . Pour le format à virgule flottante en double précision , chaque nombre est représenté par 64 bits disposés en mémoire comme suit:

entrer la description de l'image ici

La valeur réelle peut alors être trouvée avec la formule suivante:

entrer la description de l'image ici

Ce format permet des représentations numériques comsockets entre 10 ^ -308 et 10 ^ 308. Pour MATLAB, vous pouvez obtenir ces limites à partir de realmin et realmax :

 >> realmin ans = 2.225073858507201e-308 >> realmax ans = 1.797693134862316e+308 

Comme il existe un nombre fini de bits utilisés pour représenter un nombre à virgule flottante, il y a seulement un nombre fini de nombres pouvant être représentés dans la plage donnée ci-dessus. Les calculs aboutissent souvent à une valeur qui ne correspond pas exactement à l'une de ces représentations finies, de sorte que les valeurs doivent être arrondies. Ces erreurs de précision de la machine se manifestent de différentes manières, comme indiqué dans les exemples ci-dessus.

Afin de mieux comprendre ces erreurs d'arrondi, il est utile d'examiner la précision relative en virgule flottante fournie par la fonction eps , qui quantifie la distance entre un nombre donné et la représentation en virgule flottante suivante:

 >> eps(1) ans = 2.220446049250313e-16 >> eps(1000) ans = 1.136868377216160e-13 

Notez que la précision est relative à la taille d'un nombre donné représenté; les nombres plus grands auront des distances plus grandes entre les représentations à virgule flottante et auront donc moins de chiffres de précision suivant le point décimal. Cela peut être une considération importante avec certains calculs. Prenons l'exemple suivant:

 >> format long % Display full precision >> x = rand(1, 10); % Get 10 random values between 0 and 1 >> a = mean(x) % Take the mean a = 0.587307428244141 >> b = mean(x+10000)-10000 % Take the mean at a different scale, then shift back b = 0.587307428244458 

Notez que lorsque nous décalons les valeurs de x de la plage [0 1] à la plage [10000 10001] , calculons une moyenne, puis soustrayons le décalage moyen pour la comparaison, nous obtenons une valeur qui diffère pour les 3 derniers chiffres significatifs. Cela montre comment un décalage ou une mise à l'échelle des données peut modifier la précision des calculs effectués, ce qui doit être pris en compte avec certains problèmes.

Regardez cet article: Les périls de la virgule flottante . Bien que ses exemples se trouvent dans FORTRAN, cela a du sens pour pratiquement tous les langages de programmation modernes, y compris MATLAB. Votre problème (et sa solution) est décrit dans la section “Comparaisons sûres”.

type

 format long g 

Cette commande affichera la valeur complète du nombre. Ce sera probablement quelque chose comme 24.00000021321! = 24.00000123124

Essayez d’écrire

0.1 + 0.1 + 0.1 == 0.3.

Attention: Vous pourriez être surpris du résultat!

Peut-être que les deux nombres sont vraiment 24.0 et 24.000000001 mais vous ne voyez pas toutes les décimales.

Découvrez la fonction EPS Matlab .

Matlab utilise des calculs à virgule flottante jusqu’à 16 chiffres de précision (seuls 5 sont affichés).