2010-02-10 12 views
8

Usar Java en una PC con Windows 7 (no estoy seguro si eso importa) y llamar a Math.cos() con valores que deberían devolver 0 (como pi/2) devuelve valores pequeños, pero valores pequeños que, a menos que no lo entiendan, son mucho mayores que 1 ulp de cero.Java Math.cos() El método no devuelve 0 cuando se esperaba

Math.cos(Math.PI/2) = 6.123233995736766E-17 
Math.ulp(Math.cos(Math.PI/2)) = 1.232595164407831E-32 

¿Esto es, de hecho, dentro de 1 ulp y estoy simplemente confundido? ¿Y sería este un método de envoltura aceptable para resolver esta pequeña inexactitud?

public static double cos(double a){ 
    double temp = Math.abs(a % Math.PI); 
    if(temp == Math.PI/2) 
     return 0; 
    return Math.cos(a); 
} 
+0

@ dimo414 : Primero un comentario sobre su "no estoy seguro si eso importa". El comportamiento de la mayoría de las operaciones matemáticas no está definido con precisión, por lo que el sistema operativo y la CPU pueden ser importantes. Si desea una operación matemática cuyo comportamiento esté estrictamente definido (que posiblemente sea más fácil de solucionar), debe usar StrictMath, no matemática (por supuesto, las operaciones StrictMath probablemente serán más lentas porque no pueden usar la operación acelerada de hardware disponible en la CPU). – SyntaxT3rr0r

+0

Math.cos() es solo un contenedor para StrictMath.cos(), que a su vez es una función nativa. – dimo414

Respuesta

10

No olvides que Math.PI/2 es una aproximación. No va a ser exactamente pi/2, por lo que el resultado de cos(Math.PI/2) no va a ser exactamente 0. Math.cos podría estar regresando una versión bastante exacta del coseno del valor exacto devuelto por el cálculo Math.PI/2.

+0

Muy cierto, pero dicho esto, me parece intuitivo que si Math.PI es el doble más cercano a Pi, entonces Math.PI/2 debería ser el doble más cercano a Pi/2. Y dado que por la definición de cos() Pi/2 y 3Pi/2 son cero, parece que cuando la aproximación doble más cercana de esos números se pasa a Math.cos(), también debería ser cero. Tal vez esto es simplemente malo, (que es el quid de mi pregunta) pero eso tiene sentido intuitivo para mí. – dimo414

7

Nunca debe usar == con dobles. Siempre debe hacer dentro de un margen de error. 10 -17 es una buena precisión si me preguntas. Ulp figura de 10 -32 es exactamente del doble que está en 10 -17 orden de magnitud, como 2.220446049250313E-16 es la precisión del número en 10 magnitud.

+0

@Slartibartfsat: +1, exactamente. Me pegaste a eso. – SyntaxT3rr0r

+0

El margen de error debe ser al menos dos veces ulp (PI/2), porque esa es la imprecisión de PI/2. cos tiene una derivada de -1 en ese punto, por lo que la imprecisión de PI/2 se refleja en el resultado, más la imprecisión de cos. – starblue

+0

Normalmente estoy de acuerdo con usted, pero quiero limitar mi excepción a la menor cantidad de casos posible, de modo que si la entrada es par/muy/ligeramente fuera de Math.PI/2, entonces lo dejaré calcular de forma nativa, pero en el caso especial donde es exactamente Math.PI/2 o uno de sus múltiplos, me gustaría que fuera exactamente cero. Algunas pruebas limitadas han demostrado que funciona, al menos en mi computadora. ¿Está bien este razonamiento, o debería seguir revisando un rango? – dimo414

2

Este es un error común cuando está comenzando, este enlace tiene una discusión muy técnica de los motivos. http://docs.sun.com/source/806-3568/ncg_goldberg.html

Sin embargo, en su forma más simple, de la misma forma que no podemos representar exactamente 1/3 en el sistema decimal, hay valores que no se pueden representar con exactitud en el sistema binario

Cuestiones relacionadas