2010-04-19 6 views
14

En C++,¿Cómo puedo evitar el hecho de que en C++, sin (M_PI) no es 0?

const double Pi = 3.14159265; 
cout << sin(Pi);       // displays: 3.58979e-009 

que debe mostrar el número cero

entiendo que esto se debe a Pi se aproxima, pero ¿hay alguna manera de que pueda tener un valor de Pi codificado en mi programa que se devuelve 0 por sin (Pi)? (? Una constante diferente quizás)

En caso de que usted se está preguntando lo que estoy tratando de hacer: estoy convirtiendo polar a rectangular, y si bien hay algunos printf() trucos que pueda hacer para imprimirlo como " 0.00" , todavía no regresa constantemente los valores decentes (en algunos casos me sale '-0.00')

Las líneas que requieren el pecado y el coseno son:

x = r*sin(theta); 
y = r*cos(theta); 

por cierto: Mi rectangular -> Polar está funcionando bien ... es simplemente el Polar -> Rectangular

¡Gracias!

edición: Busco una solución para que pueda imprimir sin (un múltiplo del Pi) como un número redondo a la consola (idealmente sin un millar de declaraciones si-)

+3

3.58979e-009 está muy cerca de cero. Eso es porque el Pi que está utilizando también es un valor "aproximado" de Pi. – Akanksh

+3

sí, sé POR QUÉ está haciendo eso (lo mencioné en la publicación) ... Estoy buscando una solución – Adam

+28

[Lo que todo científico informático debería saber sobre la aritmética de coma flotante] (http://docs.sun.com /source/806-3568/ncg_goldberg.html) –

Respuesta

28

What Every Computer Scientist Should Know About Floating-Point Arithmetic (edit: también tengo un enlace en un comentario) es bastante hardcore (no puedo decir que lo haya leído todo), pero el quid de la cuestión es el siguiente: nunca obtendrás un punto flotante perfectamente preciso cálculos Del artículo:

Exprimir infinitamente muchos números reales en un número finito de bits requiere una representación aproximada.

No permita que su programa dependa de los resultados exactos de los cálculos de coma flotante - siempre permita un rango de tolerancia. FYI 3.58979e-009 es aproximadamente 0.0000000036. ¡Eso está dentro de cualquier rango de tolerancia razonable que elija!

+0

sí, traté de tener un umbral tal que cualquier cosa dentro de un rango muy pequeño de un entero se redondee a ese entero, pero debo haber escogido un rango demasiado grande porque seguí redondeando falsamente otros números que no tenía la intención de. – Adam

+0

'if (fabs (x - y) <0.000001)' debería hacer el truco. Será cierto si x es casi igual a y (en este caso y es 0). – AshleysBrain

+0

Esto es esencialmente lo que estaba buscando ... Hice un intento por mi cuenta pero mi constante de umbral era demasiado floja. Esto parece funcionar bastante bien. – Adam

3

3.58979e -009 esto es 0,0000000358979

Es un ~~ 0 como el suyo ~~ PI.

3

es igual a cero si su operador de igualdad tiene suficiente tolerancia

-1

Más cifras significativas podrían ayudar. Mi compilador de C (gcc) usa la constante 3.14159265358979323846 para M_PI en "math.h". Aparte de eso, no hay muchas opciones. Crear su propia función para validar la respuesta (como se describe en otra respuesta a su pregunta) es probablemente la mejor idea.

+0

¿Por qué el Python es diferente después del decimoquinto lugar decimal? –

+0

* shrug * Solo estoy proporcionando la información que tengo. Parece que Python es inexacto ... Ahora se eliminó. : P – Dustin

+0

Parece que está usando un doble para eso. Los dígitos adicionales no ayudarán en absoluto. Las cifras correctas son 3.141592653589793 2384626433 ... – EvilTeach

1

Se puede escribir un poco de función de contenedor:

double mysin(const double d) { 
    double ret = sin(d); 
    if(fabs(ret) < 0.0000001) { 
     return 0.0; 
    } else { 
     return ret; 
    } 
} 

Como otros han señalado, las matemáticas de punto flotante es notoriamente inexacta. Necesitas algún tipo de tolerancia si quieres que algo aparezca exactamente igual a cero.

+0

Es posible que también necesite una comparación para cerca de 1.0. –

+0

¿Qué pasa con los valores que deberían ser exactamente 0.5, etc.? – UncleBens

+0

Nunca obtendrás exactamente ½√3, así que al final tendrás que admitir las limitaciones del punto flotante binario. – MSalters

15

Vamos a ponerlo de esta manera, es 3.58979e-009tan cerca a 0 ya que su valor es 3.14159265 a la Pi real. Lo que tienes es, técnicamente, lo que pediste. :)

Ahora, si solo pone 9 cifras significativas (8 lugares decimales), entonces ordene que la salida tampoco se muestre más, es deciruso:

cout.precision(8); 
cout << sin(Pi); 
+2

¿Eh? Está casi exactamente a la misma distancia (como cabría esperar del teorema de Taylor). –

+0

@Mike, era solo una parábola ... en el sentido no matemático. :) OK, en interés de la ciencia, ahora es correcto. – vladr

+0

eso es lo que tengo ahora, pero sigo recibiendo -0.00 para ciertas entradas y la única forma en que puedo pensar para eludir -0.00 implica el uso de sprintf() y compararlo a "-0.00" y la codificación en el valor correcto. – Adam

1

por qué no obligan a muchos dígitos sin embargo es necesario

int isin = (int)(sin(val) * 1000); 
cout << (isin/1000.0) 
1

pecado (PI) debe ser igual a 0, por un valor exacto de la PI. No está ingresando el valor exacto de PI. Como señalan otras personas, el resultado que obtiene redondeado a 7 lugares decimales es 0, que es bastante bueno para su aproximación.

Si necesita un comportamiento diferente, debe escribir su propia función senoidal.

+4

Para obtener un valor exacto de PI y una implementación exacta de sin. Las computadoras binarias no acomodan a ninguno. –

0

Si usa flotante o doble en operaciones matemáticas, nunca tendrá resultados exactos. La razón es que en una computadora todo se almacena como una potencia de 2. Esto no se traduce exactamente a nuestro sistema de números decimales. (Un ejemplo es que no hay representación en la base 2 de 0.1)

Además, float y double son 64 bits como mínimo en algunos compiladores y plataformas. (Creo que alguien me corrige eso si es necesario). Esto causará algunos errores de redondeo para valores muy grandes o para valores muy pequeños (0,0000000000xxx)

Para obtener resultados exactos, necesitará una gran biblioteca de enteros.

como está escrito en los comentarios a la pregunta anterior ver el sitio ... http://docs.sun.com/source/806-3568/ncg_goldberg.html

4

¿Usted intentó M_PI, disponible en la mayoría de las implementaciones <cmath> o <math.h>?

Aun así, el uso de punto flotante de esta manera siempre introducirá una cierta cantidad de error.

+0

sí, desafortunadamente cout << sin (M_PI) aún no es cero – Adam

+1

@ Adam: ¡aunque está más cerca! – Potatoswatter

+5

bueno, ya sabes lo que dicen: cerrar solo cuenta en herraduras y aproximación decimal ... ¡espera un momento! – Adam

2

Puede agregar más dígitos para obtener un mejor resultado (intente, por ejemplo, 3.1415926535897932384626433832795029L), pero seguirá recibiendo errores de redondeo.

Aún así, puede crear sus propias versiones sin y cos que contrastan con su valor Pi conocido y devuelven exactamente cero en esos casos.

namespace TrigExt 
{ 
    const double PI = 3.14159265358979323846; 

    inline double sin(double theta) 
    { 
     return theta==PI?(0.0):(std::sin(theta)); 
    } 
} 

También puede ampliar esto para las otras funciones trigonométricas y para manejar múltiplos Pi.

-1

Ya sabes, solo por la corrección matemática que hay: sin (3.14159265) cero. Es aproximadamente cero, que es exactamente lo que le dice el programa. Para los cálculos, este número debería darle un buen resultado. Para mostrar, apesta, así que cada vez que imprima un flotador, asegúrese de formatear el número.

Realmente no creo que haya nada de mecánica de flotación en el trabajo aquí ... es solo matemática simple.

Sin embargo, acerca del código, tenga cuidado ... no hace que su código arroje el resultado incorrecto haciendo las aproximaciones antes de la pantalla, simplemente visualice la información de la manera correcta.

0
double cut(double value, double cutoff=1e-7) { 
    return (abs(value) > cutoff)*value; 
} 

esto pondrá a cero los valores por debajo del umbral, utilizar de esta manera cut(sin(Pi))

+0

una nota para el cauteloso: abs (doble) hace lo que se pretende en C++, pero no en C. –

3

Esto debe mostrar cero:.

cout << fixed << sin(Pi); 

(no creo que debe estar tratando de redondear nada si le preocupa mostrar, tratar con las funciones de visualización, no con el valor en sí).

+0

+1: esta es una de las pocas respuestas en la página que en realidad produce la salida deseada. El único problema es que debes agregar 0.0 a sen (Pi) para evitar obtener -0.0 como resultado en ciertos casos. Por esa razón, prefiero la respuesta que seleccioné para ser la "respuesta aceptada". – Adam

Cuestiones relacionadas