¿Por qué 24.0000 no es igual a 24.0000 en MATLAB?

Estoy escribiendo un progtwig donde necesito eliminar puntos duplicados almacenados en una matriz. El problema es que cuando se trata de verificar si esos puntos están en la matriz, MATLAB no puede reconocerlos en la matriz, a pesar de que existen.

En el siguiente código, la función de intersections obtiene los puntos de intersección:

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

El resultado:

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

Dos puntos ( vertex1 y vertex2 ) deben eliminarse del resultado. Debería hacerse con los siguientes comandos:

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

Después de hacer eso, tenemos este resultado inesperado:

 >> points points = 33.0000 24.0000 

El resultado debe ser una matriz vacía. Como puede ver, el primer (o segundo?) Par de [33.0000 24.0000] ha sido eliminado, pero no el segundo.

Luego revisé estas dos expresiones:

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

¿Cuál es el problema?


Más sorprendentemente, hice un nuevo script que solo tiene estos comandos:

 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)), :); 

El resultado como se esperaba:

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

El problema que tiene se relaciona con la forma en que se representan los números de coma flotante en una computadora. Una discusión más detallada de las representaciones de coma flotante aparece hacia el final de mi respuesta (la sección “Representación de punto flotante”). La versión TL; DR : debido a que las computadoras tienen cantidades finitas de memoria, los números solo se pueden representar con precisión finita. Por lo tanto, la precisión de los números de coma flotante se limita a un cierto número de decimales (alrededor de 16 dígitos significativos para los valores de precisión doble , el valor predeterminado utilizado en MATLAB).

Exactitud real frente a visualizada

Ahora, para abordar el ejemplo específico en la pregunta … mientras que 24.0000 y 24.0000 se muestran de la misma manera, resulta que en este caso difieren en cantidades decimales muy pequeñas. No lo ves porque MATLAB solo muestra 4 dígitos significativos por defecto , manteniendo la pantalla ordenada y ordenada. Si desea ver la precisión completa, debe emitir el comando de format long o ver una representación hexadecimal del número:

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

Valores inicializados versus valores calculados

Como solo hay un número finito de valores que se pueden representar para un número de punto flotante, es posible que un cálculo resulte en un valor que se encuentra entre dos de estas representaciones. En tal caso, el resultado debe redondearse a uno de ellos. Esto introduce un pequeño error de precisión de la máquina . Esto también significa que inicializar un valor directamente o mediante algún cálculo puede dar resultados ligeramente diferentes. Por ejemplo, el valor 0.1 no tiene una representación exacta en coma flotante (es decir, se redondea ligeramente), y así se obtienen resultados contraintuitivos como este debido a la forma en que se acumulan los errores de redondeo:

 >> 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 

Cómo manejar correctamente las comparaciones de coma flotante

Como los valores de coma flotante pueden diferir en cantidades muy pequeñas, cualquier comparación debe hacerse verificando que los valores estén dentro de algún rango (es decir, tolerancia) entre ellos, en lugar de ser exactamente iguales entre sí. Por ejemplo:

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

mostrará "Igual!"

Luego puede cambiar su código a algo como:

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

Representación de coma flotante

Una buena visión general de los números de coma flotante (y específicamente el estándar IEEE 754 para la aritmética de coma flotante ) es lo que todo científico debería saber sobre la aritmética de punto flotante por David Goldberg.

Un número de punto flotante binario está representado por tres enteros: un signo de bit s , un significado (o coeficiente / fracción) b , y un exponente e . Para el formato de punto flotante de doble precisión , cada número se representa mediante 64 bits distribuidos en la memoria de la siguiente manera:

enter image description here

El valor real se puede encontrar con la siguiente fórmula:

enter image description here

Este formato permite representaciones numéricas en el rango de 10 ^ -308 a 10 ^ 308. Para MATLAB puedes obtener estos límites de realmin y realmax :

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

Como hay un número finito de bits para representar un número de punto flotante, solo hay muchos números finitos que se pueden representar dentro del rango dado anteriormente. Los cálculos a menudo dan como resultado un valor que no coincide exactamente con una de estas representaciones finitas, por lo que los valores deben redondearse. Estos errores de precisión de la máquina se hacen evidentes de diferentes maneras, como se discute en los ejemplos anteriores.

Para comprender mejor estos errores de redondeo, es útil observar la precisión relativa del punto flotante proporcionada por la función eps , que cuantifica la distancia desde un número dado hasta la siguiente representación de coma flotante más grande:

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

Observe que la precisión es relativa al tamaño de un número dado que se representa; los números más grandes tendrán distancias más grandes entre las representaciones de coma flotante, y por lo tanto tendrán menos dígitos de precisión después del punto decimal. Esto puede ser una consideración importante con algunos cálculos. Considere el siguiente ejemplo:

 >> 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 

Tenga en cuenta que cuando cambiamos los valores de x del rango [0 1] al rango [10000 10001] , calculamos un promedio, luego restamos el desplazamiento promedio para comparar, obtenemos un valor que difiere para los últimos 3 dígitos significativos. Esto ilustra cómo un desplazamiento o escalado de datos puede cambiar la precisión de los cálculos realizados en él, que es algo que debe tenerse en cuenta con ciertos problemas.

Mira este artículo: Los peligros del punto flotante . Aunque sus ejemplos están en FORTRAN, tiene sentido para prácticamente cualquier lenguaje de progtwigción moderno, incluido MATLAB. Su problema (y la solución) se describe en la sección “Comparaciones seguras”.

tipo

 format long g 

Este comando mostrará el valor COMPLETO del número. ¡Es probable que sea algo así como 24.00000021321! = 24.00000123124

Intenta escribir

0.1 + 0.1 + 0.1 == 0.3.

Advertencia: ¡puede que te sorprenda el resultado!

Tal vez los dos números sean realmente 24.0 y 24.000000001 pero no está viendo todos los lugares decimales.

Consulte la función de Matlab EPS .

Matlab usa matemáticas de coma flotante de hasta 16 dígitos de precisión (solo se muestran 5).