Pourquoi est-24,0000 pas égal à 24,0000 dans Matlab?
-
22-08-2019 - |
Question
Je suis en train d'écrire un programme où je dois supprimer des points en double stockés dans une matrice. Le problème est que quand 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
) devraient être éliminés du résultat. Il doit ê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 avoir fait cela, nous avons ce résultat inattendu:
>> points
points =
33.0000 24.0000
Le résultat doit être une matrice vide. Comme vous pouvez le voir, la première (ou seconde?) Paire de [33.0000 24.0000]
a été éliminé, mais pas le second.
Alors 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?
De façon plus surprenante, 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 comme prévu:
>> points
points =
Empty matrix: 0-by-2
La solution
Le problème que vous rencontrez concerne la façon dont à virgule flottante numéros sont représentés sur un ordinateur. Une discussion plus détaillée des représentations à virgule flottante apparaît vers la fin de ma réponse (La section « représentation flottante point »). TL; DR Version: parce que les ordinateurs ont des quantités limitées de mémoire, le nombre ne peut être représenté avec une précision finie. Ainsi, la précision des nombres à virgule flottante est limité à un certain nombre de décimales (environ 16 chiffres significatifs pour valeurs à double précision , la valeur par défaut utilisé dans Matlab).
Actual par rapport affiché précision
Maintenant, pour répondre à l'exemple spécifique de la question ... tout 24.0000
et 24.0000
sont affiche de la même manière, il se trouve qu'ils diffèrent en fait par de très petites quantités décimales dans cette Cas. Vous ne voyez pas parce que Matlab n'affiche 4 chiffres significatifs par défaut , en gardant la affichage global propre et bien rangé. Si vous voulez voir la pleine précision, 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 en fonction des valeurs calculées
Comme il n'y a qu'un nombre fini de valeurs qui peuvent être représentés pour un nombre à virgule flottante, il est possible pour un calcul pour aboutir à 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 . 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 une exact représentation à virgule flottante (il obtient légèrement arrondi), et si vous vous retrouvez avec des résultats contre-intuitifs comme celui-ci en raison de la façon ronde erreurs off 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 à virgule flottante
Etant donné que les valeurs à virgule flottante peuvent différer par de très petites quantités, des comparaisons doivent être effectuées en vérifiant que les valeurs se situent dans une certaine plage (à savoir la tolérance) les uns des autres, par opposition à exactement égales entre elles. Par exemple:
a = 24;
b = 24.000001;
tolerance = 0.001;
if abs(a-b) < tolerance, disp('Equal!'); end
affiche "l'égalité!".
Vous pouvez ensuite modifier votre code à 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 le norme IEEE 754 pour l'arithmétique à virgule flottante ) est Ce que tout informaticien doivent savoir au sujet flottant -Point arithmétique par David Goldberg.
Un nombre à virgule flottante binaire est en fait représentée par trois nombres entiers: un bit de signe s
, une mantisse (ou coefficient / fraction) b
, et un e
d'exposant. Pour le format double précision à virgule flottante , chaque nombre est représenté par 64 bits établis dans la mémoire comme suit:
La valeur réelle peut être trouvé avec la formule suivante:
Ce format permet de représentations numériques dans la plage 10 ^ ^ -308 à 10 308. Pour Matlab vous pouvez obtenir ces limites de realmin
et realmax
:
>> realmin
ans =
2.225073858507201e-308
>> realmax
ans =
1.797693134862316e+308
Comme il y a un nombre fini de bits utilisés pour représenter un nombre à virgule flottante, il n'y a que tant de nombres finis qui peuvent être représentés dans la plage donnée ci-dessus. Entraîneront souvent Computations une valeur qui ne correspond pas exactement à l'une de ces représentations finies, de sorte que les valeurs doivent être arrondies. Ces rel="noreferrer"> se mettent en évidence de différentes manières, comme nous le verrons dans les exemples ci-dessus.
Afin de mieux comprendre ces erreurs d'arrondi, il est utile de regarder la précision en virgule flottante par rapport fourni par la fonction
Autres conseils
Regardez cet article: Les Périls de à virgule flottante. Bien que ses exemples sont en Fortran, il a un sens pour pratiquement toutes les langues de programmation modernes, y compris Matlab. Votre problème (et solution pour elle) est décrite dans la section « Comparaisons sûres ».
type
format long g
Cette commande affichera la pleine valeur du nombre. Il est susceptible d'être 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 chiffres sont vraiment 24,0 et 24,000000001 mais vous ne voyez pas toutes les décimales.
Consultez la Matlab fonction EPS .
Matlab utilise les mathématiques à virgule flottante jusqu'à 16 chiffres de précision (seulement 5 sont affichés).