floating point - ¿Por qué no puedo comparar reales en ML estándar?
floating-point sml (1)
¿Por qué no funciona
1.0 = 2.0
? ¿No es real un tipo de igualdad?
No. La variable de tipo
''''Z
indica que los operandos de
=
deben tener tipos de igualdad.
¿Por qué los reales en patrones no funcionan [...]?
La coincidencia de patrones se basa implícitamente en las pruebas de igualdad.
El error de
syntax error: inserting EQUALOP
mensaje de error críptico
syntax error: inserting EQUALOP
indica que el analizador SML / NJ no permite literales de punto flotante donde se espera un patrón, por lo que el programador no puede recibir un error de tipo más significativo.
Elaborar,
De http://www.smlnj.org/doc/FAQ/faq.txt :
P: ¿Es real un tipo de igualdad?
R: Estaba en SML ''90 y SML / NJ 0.93, pero no está en SML ''97 y SML / NJ 110. Entonces
1.0 = 1.0
causará un error de tipo porque "=" exige argumentos que tienen un tipo de igualdad. Además, los literales reales no se pueden usar en patrones.
De http://mlton.org/PolymorphicEquality :
El único tipo de terreno que no se puede comparar es real. Entonces,
13.0 = 14.0
no es de tipo correcto. Se puede usarReal.==
para comparar reales para la igualdad, pero tenga en cuenta que esto tiene propiedades algebraicas diferentes a la igualdad polimórfica.
Por ejemplo,
Real.== (0.1 + 0.2, 0.3)
es
false
.
De http://sml-family.org/Basis/real.html :
Decidir si real debería ser un tipo de igualdad y, en caso afirmativo, qué significa igualdad también era problemático. IEEE especifica que el signo de ceros debe ignorarse en las comparaciones, y que la igualdad se evalúa como falsa si cualquiera de los argumentos es NaN.
Estas restricciones son inquietantes para el programador SML. Lo primero implica que 0 = ~ 0 es verdadero mientras que r / 0 = r / ~ 0 es falso. Esto último implica anomalías como r = r es falso, o que, para una celda de referencia rr, podríamos tener rr = rr pero no tener! Rr =! Rr. Aceptamos la comparación sin signo de ceros, pero consideramos que la propiedad reflexiva de la igualdad, la igualdad estructural y la equivalencia de <> y no o = debe ser preservada.
La versión corta: no compares reales usando la igualdad. Realice una prueba de épsilon . Yo recomendaría leer el artículo en http://floating-point-gui.de/errors/comparison . En resumen:
-
No compruebe si los reales son iguales, pero si la diferencia es muy pequeña.
-
El margen de error con el que se compara la diferencia ( delta ) a menudo se denomina épsilon .
-
No compare la diferencia con un épsilon fijo:
fun nearlyEqual (a, b, eps) = Real.abs (a-b) < eps
-
No solo compare la diferencia relativa con epsilon :
fun nearlyEqual (a, b, eps) = abs ((a-b)/b) < eps
-
Esté atento a los casos límite:
-
Cuando
b = 0.0
aumentaDiv
. (Cambiarb
proporciona un caso de borde simétrico). -
Cuando
b
están en lados opuestos de cero, devuelvefalse
incluso cuando son los números distintos de cero más pequeños posibles. -
El resultado no es conmutativo. Hay casos en los que
nearlyEqual (a, b, eps)
no da el mismo resultado quenearlyEqual (b, a, eps)
.
-
La guía proporciona una solución genérica; traducido a ML estándar esto se ve así:
fun nearlyEqual (a, b, eps) =
let val absA = Real.abs a
val absB = Real.abs b
val diff = Real.abs (a - b)
in Real.== (a, b) orelse
( if Real.== (a, 0.0) orelse
Real.== (b, 0.0) orelse
diff < Real.minNormalPos
then diff < eps * Real.minNormalPos
else diff / Real.min (absA + absB, Real.maxFinite) < eps )
end
Y continúa advirtiendo sobre algunos casos extremos:
Hay algunos casos en los que el método anterior aún produce resultados inesperados (en particular, es mucho más estricto cuando un valor es casi cero que cuando es exactamente cero), y algunas de las pruebas para las que se desarrolló probablemente especifiquen comportamientos que no son apropiados para algunas aplicaciones ¡Antes de usarlo, asegúrese de que sea apropiado para su aplicación!
-
¿Por qué no funciona
1.0 = 2.0
? ¿No es real un tipo de igualdad?Da el error:
Error: operator and operand don''t agree [equality type required] operator domain: ''''Z * ''''Z operand: real * real in expression: 1.0 = 2.0
-
¿Por qué los reales en patrones no funcionan así?
fun fact 0.0 = 1.0 | fact x = x * fact (x - 1.0)
Da el error:
Error: syntax error: inserting EQUALOP