web-dev-qa-db-fra.com

Pourquoi 24,0000 n'est-il pas égal à 24,0000 dans MATLAB?

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

Dans le code suivant, la fonction 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) doit être éliminé 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 matrice vide. Comme vous pouvez le voir, la première (ou la deuxième?) Paire de [33.0000 24.0000] a été éliminé, mais pas le deuxième.

J'ai ensuite 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 n'a 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 matrix: 0-by-2
66
Kamran Bigdely

Le problème que vous rencontrez concerne la façon dont 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 : parce que les ordinateurs ont des quantités de mémoire finies, 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 valeurs de double précision , la valeur par défaut utilisée dans MATLAB).

Précision réelle vs précision affichée

Maintenant, pour répondre à l'exemple spécifique de la question ... tandis que 24.0000 et 24.0000 sont affichés de la même manière, il s'avère qu'ils diffèrent en fait par de très petites quantités décimales dans ce cas. Vous ne le voyez pas parce que MATLAB n'affiche que 4 chiffres significatifs par défaut , gardant l'affichage global net et bien rangé. Si vous voulez voir la précision complète, vous doit soit émettre le format long commande ou affiche une représentation hexadécimale du nombre:

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

Valeurs initialisées vs valeurs calculées

Puisqu'il n'y a qu'un nombre fini de valeurs qui peuvent être représentées pour un nombre à virgule flottante, il est possible qu'un calcul aboutisse à une valeur qui se situe 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 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 à virgule flottante exacte (c'est-à-dire qu'elle est légèrement arrondie), et vous vous retrouvez donc avec des résultats contre-intuitifs comme celui-ci en raison des erreurs d'arrondi accumuler:

>> 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 à 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 sont dans une certaine plage (c'est-à-dire la tolérance) les unes des autres, par opposition à exactement égales les unes aux autres. Par exemple:

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

affichera "Égal!".

Vous pouvez ensuite 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 en particulier la norme IEEE 754 pour l'arithmétique à virgule flottante ) est Ce que tout informaticien devrait savoir sur l'arithmétique à virgule flottante par David Goldberg.

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

enter image description here

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

enter image description here

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

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

Puisqu'il existe un nombre fini de bits utilisés pour représenter un nombre à virgule flottante, il y a seulement autant de nombres finis qui peuvent ê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, donc les valeurs doivent être arrondies. Ces erreurs de précision de la machine se manifestent de différentes manières, comme expliqué dans les exemples ci-dessus.

Afin de mieux comprendre ces erreurs d'arrondi, il est utile de regarder la précision relative en virgule flottante fournie par la fonction eps , qui quantifie la distance d'un nombre donné au suivant plus grande représentation en virgule flottante:

>> 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 après la virgule décimale. 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], calculez une moyenne, puis soustrayez le décalage moyen pour comparaison, nous obtenons une valeur qui diffère pour les 3 derniers chiffres significatifs. Cela illustre 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.

92
gnovice

Regardez cet article: Les dangers du virgule flottante . Bien que ses exemples soient en FORTRAN, il convient à 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".

22
Rorick

type

format long g

Cette commande affichera la valeur complète du nombre. Il est probable que ce soit quelque chose comme 24.00000021321! = 24.00000123124

13
KitsuneYMG

Essayez d'écrire

0,1 + 0,1 + 0,1 == 0,3.

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

7
Andrey Rubshtein

Peut-être que les deux nombres sont vraiment 24,0 et 24,000000001 mais vous ne voyez pas toutes les décimales.

2
Jimmy J

Découvrez fonction Matlab EPS .

Matlab utilise des mathématiques en virgule flottante jusqu'à 16 chiffres de précision (seuls 5 sont affichés).

1
jle