Opened 13 years ago
Closed 13 years ago
#1412 closed defect (fixed)
Unexpected behavior comparing Real objects
Reported by: | Alfredo Torre | Owned by: | Víctor de Buen Remiro |
---|---|---|---|
Priority: | high | Milestone: | Mantainance |
Component: | Kernel | Version: | 2.0.1 |
Severity: | critical | Keywords: | compare, Real |
Cc: | cperez@…, tguo@… |
Description
Hello,
can you explain the following unexpected behavior comparing Real objects?
Real R001 = LE(3*0.2, 0.6);
R001 is 0
Change History (10)
comment:1 Changed 13 years ago by
comment:2 Changed 13 years ago by
Aunque supongo que no es consuelo, puedo asegurar en R pasa lo mismo
> 3*0.2 - 0.6 [1] 1.110223e-16
comment:3 Changed 13 years ago by
Con las comparaciones en R pasa lo mismo
> 3*2 <= 0.6 [1] FALSE
Llama la atención porque todo el mundo sabe que 0.2*3
es 0.6
y parecxe mentira que un ordenador capaz de hacer cálculos mucho más complicados sea incapaz de hacer correctamente una operación tan sencilla. Pero si fuera con 10 decimales nadie lo vería y pasaría desapercibido. Es el precio que se paga por poder hacer operaciones tan rápidamente. Existen aritméticas con más precisión o incluso con precisión infinita, pero son muchísimo más lentas.
Yo lo que suelo hacer es comparar los números con una tolerancia dada, por ejemplo, para números cercanos a la unidad sería
Real LE(3*0.2-0.6, MachineEpsilon)
Si en tu caso concreto basta con menos tolerancia en la comparación puedes poner cosas como
Real LE(3*0.2-0.6, 1.E-8)
También sirve sumarle una constante a ambos lados de la comparación
Real LE(3*0.2 +1, 0.6 +1)
comment:5 Changed 13 years ago by
Sería una locura crear una variable global de Tolerancia para las funciones tipo LT, LE, ...?
comment:6 Changed 13 years ago by
A mí me parece sumamente peligroso pues luego se te olvida que has puesto una tolerancia y en otro sitio necesitas otras. Es el tipo de cosas que hacen difícil detectar y reproducir los problemas. Yo no conozco ningún sistema serio que lo haga así.
comment:7 follow-up: 9 Changed 13 years ago by
Curioso lo los ordenadores...
Me pregunto si tendría sentido que TOL resolviera internamente la expresión anterior como:
Real R001 = LE(3*0.2, 0.6); Real R002 = LE(3*0.2 - 0.6, MachineEpsilon);
En este caso R002 está haciendo lo mismo que R001 pero devuelve el resultado esperado. ¿Tendría alguna contraindicación?
Un saludo.
comment:8 Changed 13 years ago by
Sobre decirlo, pero para el operador "mayor o igual que" sería lo siguiente:
Real a = 3*0.2; Real b = 0.6; Real G000 = GE(a, b); Real G001 = GE(a - b, -MachineEpsilon); Real G002 = GE(b, a); Real G003 = GE(b - a, -MachineEpsilon); Real L000 = LE(a, b); Real L001 = LE(a - b, MachineEpsilon); Real L002 = LE(b, a); Real L003 = LE(b - a, MachineEpsilon);
Lógicamente esto sólo funcionaría para diferencias mayores que el MachineEpsilon. En este caso fallarían los dos métodos:
Real c = 0.6+10^(-18); Real d = 0.6; Real G012 = GE(b, a); Real G013 = GE(b - a, -MachineEpsilon); //Falla
Un saludo.
comment:9 Changed 13 years ago by
¿Tendría alguna contraindicación?
La contraindicación es lo que digo en el comentario anterior. Si ya me parece peligroso usar una variable global para controlar la tolerancia, no digamos ya dejarla fija. En cada ocasión se puede necesitar una tolerancia distinta.
comment:10 Changed 13 years ago by
Resolution: | → fixed |
---|---|
Status: | new → closed |
Son los problemas típicos de la aritmética discreta, pues en contra delo que pudiera parecer, la operación
0.2*3
en coma flotante con 64 bits no es0.6
sino0.6000000000000001
. Y la diferencia entre ambas cantidades no es 0 sinoque es un número más pequeño que el epsilon de la máquina
En general los problemas de frontera no son resolubles con aritmética discreta.