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

Resuelto Kamran Bigdely asked hace 15 años • 5 respuestas

Estoy escribiendo un programa en el que necesito eliminar puntos duplicados almacenados en una matriz. El problema es que a la hora de comprobar si esos puntos están en la matriz, MATLAB no puede reconocerlos en la matriz aunque existen.

En el siguiente código, intersectionsla función 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

Se deben eliminar dos puntos ( vertex1y vertex2) del resultado. Debe hacerse mediante 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 debería ser una matriz vacía. Como puedes ver, el primer (¿o segundo?) par [33.0000 24.0000]ha sido eliminado, pero no el segundo.

Luego verifiqué 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?


Lo más sorprendente es que creé 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
Kamran Bigdely avatar Mar 26 '09 23:03 Kamran Bigdely
Aceptado

El problema que tiene se relaciona con cómo se representan los números de punto flotante en una computadora. Hacia el final de mi respuesta aparece una discusión más detallada sobre las representaciones de punto flotante (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 punto flotante está limitada a un cierto número de decimales (alrededor de 16 dígitos significativos para valores de doble precisión , el valor predeterminado utilizado en MATLAB).

Precisión real versus mostrada

Ahora, para abordar el ejemplo específico de la pregunta... si bien 24.0000y 24.0000se muestran de la misma manera, resulta que en este caso en realidad difieren en cantidades decimales muy pequeñas. No lo ve porque MATLAB solo muestra 4 dígitos significativos de forma predeterminada , manteniendo la visualización general limpia y ordenada. Si desea ver la precisión completa, debe emitir el format longcomando 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 frente a valores calculados

Dado que sólo hay un número finito de valores que se pueden representar para un número de punto flotante, es posible que un cálculo dé como resultado un valor que se encuentre 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.1no tiene una representación exacta de punto flotante (es decir, se redondea ligeramente), por lo que terminas con resultados contrarios a la intuición 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 punto flotante

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

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

mostrará "¡Igual!".

Luego podrías cambiar tu código a algo como:

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

Representación de punto flotante

Una buena descripción general de los números de punto flotante (y específicamente del estándar IEEE 754 para aritmética de punto flotante ) es Lo que todo informático debería saber sobre la aritmética de punto flotante, de David Goldberg.

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

ingrese la descripción de la imagen aquí

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

ingrese la descripción de la imagen aquí

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

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

Dado que se utiliza un número finito de bits para representar un número de punto flotante, solo hay un número limitado de números finitos que se pueden representar dentro del rango dado anteriormente. Los cálculos a menudo darán 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 mecánica se hacen evidentes de diferentes maneras, como se analiza en los ejemplos anteriores.

Para comprender mejor estos errores de redondeo, es útil observar la precisión relativa de punto flotante proporcionada por la función eps, que cuantifica la distancia desde un número determinado hasta la siguiente representación de punto 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 determinado que se representa; los números más grandes tendrán distancias mayores entre representaciones de punto flotante y, por lo tanto, tendrán menos dígitos de precisión después del punto decimal. Esta puede ser una consideración importante en 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 xdel rango [0 1]al rango [10000 10001], calculamos una media y luego restamos el desplazamiento de la media para comparar, obtenemos un valor que difiere en 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 ellos, algo que debe tenerse en cuenta en ciertos problemas.

gnovice avatar Mar 26 '2009 16:03 gnovice

Tipo

format long g

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

KitsuneYMG avatar Mar 26 '2009 21:03 KitsuneYMG

Intenta escribir

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

Advertencia: ¡puede que te sorprenda el resultado!

Andrey Rubshtein avatar Dec 14 '2011 18:12 Andrey Rubshtein

Quizás los dos números sean en realidad 24,0 y 24,000000001, pero no ves todos los decimales.

Jimmy J avatar Mar 26 '2009 16:03 Jimmy J