2011-05-21 20 views
8

Estoy intentando algo que pensé que debería ser razonablemente simple. Tengo un ángulo, una posición y una distancia y quiero encontrar las coordenadas X, Y a partir de esta información.Math.Cos & Math.Sin in C#

Con una entrada ejemplo de 90 grados que convertir el valor en radianes con el siguiente código:

public double DegreeToRadian(float angle) 
{ 
    return Math.PI * angle/180.0; 
} 

Esto me da 1.5707963267949 radianes Entonces cuando uso

Math.Cos(radians) 

termino con una respuesta de: 6.12303176911189E-17

¿Qué diablos está pasando? El coseno de 90 grados debería ser 0, entonces ¿por qué estoy obteniendo tal desviación ... y más importante aún cómo puedo detenerlo?

+0

Definitivamente se redondea a 0. Utilice un especificador de formato cuando convierta el valor en una cadena para que el usuario lo vea de la manera que espera. –

+0

Ok, entiendo que la falta de precisión se debe a la falta de precisión en los tipos de coma flotante, pero ¿cómo algo como la Calculadora de Windows logran dar por muerta la respuesta, simplemente hace trampa y usa una tabla de búsqueda? – elaverick

+1

@elaverick: quien dice que la Calculadora de Windows usa .Net double (no lo es) o cualquier tipo de coma flotante (no lo es). ¿Y quién dice que está produciendo los resultados precisos de un cálculo en lugar de aplicar reglas de redondeo sensatas? –

Respuesta

13

Déjame responder tu pregunta con otro: ¿Qué tan lejos crees que es 6.12303176911189E-17 de 0? Lo que usted llama deviance se debe en realidad a la forma en que los números de punto flotante se almacenan internamente. Te recomendaría que lees el following article. En .NET se almacenan usando el IEEE 754 standard.

+2

Aplicando algo de trigonometría básica, la distancia entre dos partículas que viajan un año luz con ese error en el ángulo es 6.1E-17 * 9.4E15 ~ = 0.57 = 57cm. No creo que valga la pena preocuparse demasiado por eso tampoco. –

2

Lea en floating point arithmetic. Nunca es y nunca puede ser exacto. Nunca compare exactamente con nada, pero verifique si los números difieren en un (pequeño) épsilon.

+3

A veces es exacto. Simplemente no todo el tiempo. –

+0

Es exactamente tan exacto como cualquier otra cosa en una computadora que no usa aleatoriedad. El problema es la fijación general de la mayoría de las personas (incluidos los programadores informáticos) con representaciones decimales. –

+2

@Damien_The_Unbeliever Completamente y completamente equivocado. Es inexacto porque el conjunto de números reales no se puede representar con precisión a través de * cualquier * medio físico; no tiene nada que ver con la base numérica. Otras cosas en una computadora pueden representarse exactamente ... enteros y números racionales, por ejemplo. Incluso el problema aquí se puede expresar exactamente a través de una representación simbólica en lugar de numérica, pero eso solo puede llevarlo hasta ahora. –

5

Ver respuestas arriba. Recuerde que 6.12303176911189E-17 es 0.00000000000000006 (¡Es posible que haya olvidado un cero allí!) Por lo que es una desviación muy, muy pequeña.

1

Dado que el resultado del cálculo es muy cercano a 0 (cero), usted podría utilizar el redondeo:

Math.Round(result, 4): // 4 decimals, e.g.: 2.1234 

Así, cálculo de seno/coseno de Radian:

const double Deg = Math.PI/180; 
double sin = Math.Round(Math.Sin(yourRadianValue * Deg), 4); 
double cos = Math.Round(Math.Cos(yourRadianValue * Deg), 4); // 0.0000...06 becomes 0 

Que si yourRadianValue = 90, devuelve sin = 1 y cos = 0.

1

Las otras publicaciones son correctas acerca de la práctica de trabajar con implementaciones de punto flotante que devuelven resultados con pequeños errores. Sin embargo, sería bueno si las implementaciones de biblioteca punto flotante preservaría la identidad básica de las funciones conocidas:

Math.Sin(Math.PI) debe igual 0,
Math.Cos(Math.PI) debe igual -1,
Math.Sin(Math.PI/2) debe igual 1 ,
Math.Cos(Math.PI/2) debe igual 0, etc.

Es de esperar que una biblioteca de coma flotante respete estos y otros trigonometric identities, cualesquiera que sean los pequeños errores en sus valores constantes (p. Math.PI).

El hecho de que obtiene un pequeño error de Math.Cos(Math.PI/2) indica que la implementación está calculando el resultado, en lugar de extraerlo de una tabla. Una mejor implementación de Math.Cos y otras funciones trascendentales podrían ser más precisas para identidades específicas.

Estoy seguro de que en el caso de C#, se espera este comportamiento, por lo que Microsoft no podría cambiarlo sin afectar el código existente. Si le interesa obtener el resultado preciso para las identidades trigonométricas específicas, puede envolver las funciones nativas de coma flotante con algún código que verifique las entradas conocidas.