Prueba de igualdad de dos flotadores: ejemplo realista

8

¿Cuándo tiene sentido en la programación probar la igualdad de dos números de coma flotante?

es decir

a == b 

donde tanto a & b son flotadores.

Mi ingenua impresión es que uno siempre probaría la diferencia contra alguna tolerancia épsilon.

¿Me equivoco? ¿Puede probar la igualdad de las carrozas ser significativo en ciertos contextos?

¿Algún ejemplo de la naturaleza? es decir, desde bases de código reales o aplicaciones disponibles en git, etc.

PD. Estoy implícitamente asumiendo que usar el operador de igualdad en flotadores es realmente significativo en algunos contextos; de lo contrario, ¿por qué la mayoría de los lenguajes de programación lo permitirían?

curioso_cat
fuente
1
Como se trata de problemas prácticos con algoritmos numéricos, estoy migrando a la ciencia computacional .
Puede ser útil saber qué problema está tratando de resolver haciendo esta pregunta en particular. meta.stackexchange.com/q/66377
Kirill
1
@Krill El problema es la curiosidad. Conozco los consejos sobre cuándo NO usarlo. Pero si el operador todavía está permitido, debe haber casos en los que sea correcto usarlo. Pero esos casos no eran obvios. Entonces quería saber. Y las respuestas traen algunos buenos ejemplos.
curious_cat

Respuestas:

5

Mi ingenua impresión es que uno siempre probaría la diferencia contra alguna tolerancia épsilon.

Una implementación no ingenua de esta idea probablemente debería aprovechar el operador de comparación de igualdad para manejar los casos especiales importantes que contempla el estándar IEEE 754 (infinitos, números desnormalizados ...).

Echa un vistazo a ¿Cómo debo hacer la comparación de punto flotante? :

...

if (a == b)  // shortcut, handles infinities
  return true;

if (a == 0 || b == 0 || diff < Float.MIN_NORMAL) {
  // a or b is zero or both are extremely close to it
  // relative error is less meaningful here

...


A veces realmente hay una respuesta que es correcta y quieres igualdad exacta. Probar la exactitud de una implementación es un buen ejemplo (es decir , solo hay cuatro mil millones de flotadores, ¡así que pruébelos a todos! ).

manlio
fuente
¿La prueba para a == 0 dentro de la cláusula OR es redundante por diff <Float.MIN_NORMAL?
curious_cat el
3

Un ejemplo obvio donde ==está bien, es cuando ay bes el mismo es decir, número a=c; b=c, por ejemplo para verificar si ay bse inicializa la misma manera. Por supuesto, |a-b| < epsilontambién funcionaría aquí. El único problema es, ¿qué tan pequeño es epsilon?

Además, a == bse compilaría en una sola instrucción, mientras |a-b| < epsilonque tomaría bastantes.

Karolis Juodelė
fuente
¡Gracias! ¿Cuándo querría uno, en una situación de codificación típica, saber si a y b se inicializaron de la misma manera? ¿Puedes publicar fragmentos de código con esto? Quiero decir, ¿no podría uno simplemente examinar la fuente y contar?
curious_cat
1
@curious_cat suponga que está haciendo un juego como Minecraft, con una cuadrícula de fichas, suponga que estas fichas tienen coordenadas flotantes, por cualquier razón, y se inicializaron en un bucle obvio como for x in [0..n] step w: for y in [0..n] step w: add_tile(x, y). En ese caso, t1.x == t2.xes una forma perfectamente segura de probar si las dos fichas están en un plano. Solo necesita |a-b|<epsiloncuándo ay bson resultados de diferentes cálculos que supuestamente obtendrían el mismo valor. Pero muy a menudo tiene un resultado de un cálculo almacenado en dos variables, o sobrescribe variables con constantes.
Karolis Juodelė
Muy buen ejemplo! Gracias. Tiene mucho sentido. El primer caso de uso que suena real que obtuve realmente ¡Deberías agregar eso a tu respuesta!
curious_cat el
2

Un ejemplo extremo: IBM fue la primera compañía que construyó procesadores con una instrucción fusionada de adición múltiple. Utilizando esa instrucción, crearon un método muy rápido para calcular raíces cuadradas de acuerdo con el estándar IEEE-754. Este método falla para un solo valor de entrada 1 ≤ x <4: si x es el número más grande representable como un número de coma flotante que es menor que 4, entonces el resultado se redondeará incorrectamente.

Entonces, en algún lugar de su implementación, verifican si x es igual a ese valor específico. Quieren reconocer ese valor, y no otros.

gnasher729
fuente
¡Gracias! ¿Qué valor tiene esa idea? No pude encontrar ninguna referencia para leer sobre esto. ¿Puedes publicar algún enlace?
curious_cat
2

Mi ingenua impresión es que uno siempre probaría la diferencia contra alguna tolerancia épsilon. ¿Me equivoco? ¿Puede probar la igualdad de las carrozas ser significativo en ciertos contextos?

No hay recetas únicas. En este artículo hay un tratamiento exhaustivo, donde puede encontrar una respuesta completa con código técnico y.

En resumen, hay principalmente 3 casos:

  • comparando contra cero
  • comparando con un no cero
  • comparando dos números arbitrarios

Su idea de usar una comparación contra una tolerancia es buena para algunos casos, pero también hay una técnica basada en la Unidad en último lugar ( ULP ), descrita en el artículo.

Estoy implícitamente asumiendo que usar el operador de igualdad en flotadores es realmente significativo en algunos contextos; de lo contrario, ¿por qué la mayoría de los lenguajes de programación lo permiten?

Como anteriormente, hay situaciones en las que puede usarlo, pero tenga cuidado. Por ejemplo, el compilador gcc tiene una advertencia:

warning: comparing floating point with == or != is unsafe

Actualizar

Agrego algunas consideraciones sobre este argumento y tampoco están estrictamente relacionadas con el caso a == b.

Igualdad con expresión

Considerando el caso:

a + b == c 

a b c

unasi==CFl(Fl(una)+Fl(si))==Fl(C)
  • Fl(X)X

El |unauna+siEl |mirruna+El |siuna+siEl |mirrsi
mirrX=El |X-Fl(X)El |El |XEl |

Entonces, en este caso, el uso de ==es más delicado.

Portado en diferentes ambientes

Cuando portamos un código en diferentes entornos (máquina diferente) podemos obtener un resultado diferente (por ejemplo, tratar de pensar en una prueba unitaria). También en el caso el uso de ==es delicado.

Mauro Vanzetto
fuente
Ah! Entonces genera una advertencia. Gracias. Entonces, mi impresión ahora es que en 99% de los casos de codificación, una comparación de igualdad flotante es un error, pero todavía está permitida para esos pocos casos de esquina en los que puede tener sentido.
curious_cat
1
@curious_cat Sí, estoy de acuerdo contigo. Añado alguna nota.
Mauro Vanzetto el
0

Su "impresión ingenua" no es una "impresión ingenua": es el resultado de leer sobre aritmética de coma flotante y solo comprenderla a medias. La "impresión ingenua" sería la impresión obvia de que para saber si a y b son iguales, se pregunta si son iguales.

Hay muchas situaciones en las que todo lo que necesita saber es si dos números de coma flotante son iguales o no. Hay muchas situaciones en las que sabe que no hay errores de redondeo o que no hay variaciones debido a errores de redondeo. Como convertir números decimales a coma flotante, que será determinista en cualquier implementación sensata.

Aquí hay una buena: alguien afirmó que para cualquiera de los dos números de coma flotante a, b, el resultado de (b + a + b) y (b + b + a) es el mismo y desea probar esa afirmación. Intenta hacerlo sin comparar dos números de coma flotante para la igualdad.

Aquí hay uno mejor: intente crear un conjunto de números de coma flotante.

gnasher729
fuente