2008-11-30 30 views
14

Hasta ahora he visto muchas publicaciones relacionadas con la igualdad de números en coma flotante. La respuesta estándar a una pregunta como "¿cómo deberíamos decidir si xey son iguales?" es¿Deberíamos comparar los números de coma flotante para la igualdad con un error * relativo *?

abs(x - y) < epsilon 

donde épsilon es un fijo, constante pequeña. Esto se debe a que los "operandos" xey son a menudo el resultado de algún cálculo en el que está involucrado un error de redondeo, por lo tanto, el operador de igualdad estándar == no es lo que queremos decir, y lo que realmente deberíamos preguntar es si xey son cerrar, no igual.

Ahora, siento que si x es "casi iguales" a y, a continuación, también x * 10^20 debe ser "casi igual" a Y * 10^20, en el sentido de que el error relativodebe ser el mismo (pero "relativo" a qué?). Pero con estos números grandes, la prueba anterior fallaría, es decir, esa solución no "escala".

¿Cómo lidiarías con este problema? ¿Deberíamos reescalar los números o reestructurar épsilon? ¿Cómo? (O es mi intuición mal?)

Aquí es una related question, pero no me gusta su respuesta aceptada, por lo reinterpret_cast parece un poco complicado para mí, no entiendo lo que está pasando. Intente proporcionar una prueba simple.

Respuesta

17

Todo depende del dominio del problema específico. Sí, usar el error relativo será más correcto en el caso general, pero puede ser significativamente menos eficiente ya que involucra una división adicional de coma flotante. Si conoce la escala aproximada de los números en su problema, es aceptable usar un error absoluto.

This page describe una serie de técnicas para comparar carrozas. También repasa una serie de cuestiones importantes, como las que tienen subnormales, infinitos y NaN. Es una gran lectura, recomiendo leerlo todo el tiempo.

+0

Gracias. El documento también explica las motivaciones detrás del rude cast para int (aunque en el código ordinario yo optaría por la comprensibilidad y uso una de las soluciones all-float :) –

+1

La versión actualizada del enlace de arriba es [Comparación de números de coma flotante, edición de 2012] (http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/) – sfstewman

1

El problema es que con números muy grandes, en comparación con épsilon fallará.

Tal vez una solución mejor (pero más lento) sería utilizar la división, ejemplo:

div(max(a, b), min(a, b)) < eps + 1 

Ahora el 'error' será relativa.

+0

Precisamente, es relativo al mínimo entre ayb, ¿no? –

+2

Hmmm. Tenga cuidado con las divisiones por cero :) –

+0

Y preste atención a su signo. El artículo en la respuesta de Adán sugiere una comparación relativa máxima absoluta. –

3

Como solución alternativa, ¿por qué no simplemente redondear o truncar los números y luego hacer una comparación directa? Al establecer el número de dígitos significativos por adelantado, puede estar seguro de la precisión dentro de ese límite.

+0

Redondeo y truncamiento funcionan mal. Si redondeamos (al más cercano) a tres dígitos, entonces 1.499999999999 y 1.5000000000001 se compararán con un anuncio diferente, a pesar de ser ridículamente cercanos. Si truncamos, entonces 1.999999999999 y 2.00000000000001 se compararán como diferentes a pesar de estar extremadamente cerca. Cualquier esquema de redondeo o truncamiento tendrá cúspides como esta. Cualquier solución tiene que comenzar por restar los números y luego decidir si los diferentes son lo suficientemente grandes como para ser significativos. –

+1

@BruceDawson 1.4999999999999 y 1.500000000000001 redondeados a 3 dígitos se compararán como iguales ... (ambos 1.5) – colmde

+1

Huh. No estoy seguro de por qué di esos números como ejemplos porque tienes razón, no funcionan como dije. El problema real es que puede tener dos números arbitrariamente cerrados que se alejan uno del otro y, por lo tanto, se comparan como diferentes cuando deben compararse como iguales. Para todo el año, a más cercana: 1,50500000000000001 1,50499999999999999 cuando se redondea a tres dígitos Esto nos da 1,51 y 1,50, a pesar de estar separados por menos de una parte por mil millones. Cualquier esquema de redondeo afectará este problema con números cerca de la cúspide. Es por eso que Round-and-compare está roto. –

0

Usar el error relativo es al menos no tan malo como usar errores absolutos, pero tiene problemas sutiles para valores cercanos a cero debido a problemas de redondeo. Un algoritmo lejos de ser perfecto, pero algo más robusta combina absoluto y se acerca error relativo:

boolean approxEqual(float a, float b, float absEps, float relEps) { 
    // Absolute error check needed when comparing numbers near zero. 
    float diff = abs(a - b); 
    if (diff <= absEps) { 
     return true; 
    } 

    // Symmetric relative error check without division. 
    return (diff <= relEps * max(abs(a), abs(b))); 
} 

adapté el código de excelente artículo de Bruce Dawson Comparing Floating Point Numbers, 2012 Edition, una lectura obligada para cualquier persona que hace comparaciones de punto flotante es necesario - un tema increíblemente compleja con muchas trampas

0

La mayoría de las veces, cuando el código compara valores, lo hace para responder a algún tipo de pregunta.Por ejemplo:

  1. Si sé lo que una función devuelve cuando se le da un valor de X, puedo suponer que volverá lo mismo si se les da Y?

  2. Si tengo un método para calcular una función que es lenta pero precisa, estoy dispuesto a aceptar alguna inexactitud a cambio de velocidad, y quiero probar una función candidata que parece ajustarse a la factura, son las salidas desde esa función lo suficientemente cerca de la conocida-precisa para ser considerada "correcta".

Para responder a la primera pregunta, el código idealmente debería hacer una comparación bit a bit en el valor, aunque menos que un lenguaje compatible con los nuevos operadores añadido a IEEE-754 en 2009 que pueden ser menos eficiente que el ideal. Para responder a la segunda pregunta, uno debe definir qué grado de precisión se requiere y probar en contra de eso.

No creo que haya mucho mérito en un método de propósito general que considere cosas iguales que están cerca, ya que las diferentes aplicaciones tendrán requisitos diferentes para tolerancia absoluta y relativa, basadas en qué preguntas exactas se suponen las pruebas contestar.

Cuestiones relacionadas