2010-04-19 22 views
14

In C++,Come posso aggirare il fatto che in C++, sin (M_PI) non è 0?

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

dovrebbe visualizzare il numero zero

comprendo questo è perché Pi viene approssimato, ma non v'è alcun modo per avere un valore di Pi insita nel mio programma che verrà restituisce 0 per sin (Pi)? (una costante diversa forse?)

Nel caso ti stia chiedendo cosa sto cercando di fare: sto convertendo polare in rettangolare, e mentre ci sono alcuni trucchi di printf() che posso fare per stamparlo come " 0.00" , ancora non in modo coerente restituisce i valori decenti (in alcuni casi ho "-0.00")

Le linee che richiedono il peccato e coseno sono:

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

BTW: Il mio Rettangolare -> Polar sta funzionando bene ... è solo il Polar -> Rettangolare

Grazie!

edit: Sto cercando una soluzione in modo che possa stampare il peccato (un multiplo di Pi) come un bel numero tondo alla console (idealmente senza un migliaio di dichiarazioni if-)

+3

3.58979e-009 è molto vicino allo zero. Questo perché il Pi che stai usando è anche un valore "approssimativo" di Pi. – Akanksh

+3

sì, so perché lo sta facendo (l'ho detto nel post) ... Sto cercando una soluzione alternativa – Adam

+28

[Ciò che ogni scienziato informatico dovrebbe sapere sull'aritmetica a virgola mobile] (http://docs.sun.com /source/806-3568/ncg_goldberg.html) –

risposta

28

What Every Computer Scientist Should Know About Floating-Point Arithmetic (modifica: anche collegato in un commento) è una lettura piuttosto hardcore (non posso dire di averlo letto tutto), ma il punto cruciale è questo: non avrai mai un punto di virata perfettamente preciso calcoli. Dall'articolo:

Spremere infiniti numeri reali in un numero finito di bit richiede una rappresentazione approssimativa.

Non lasciare che il programma dipenda dai risultati esatti dei calcoli in virgola mobile: consente sempre un intervallo di tolleranza. FYI 3.58979e-009 è circa 0.0000000036. Questo è bene all'interno di qualsiasi ragionevole intervallo di tolleranza che si sceglie!

+0

Sì, ho provato ad avere una soglia tale che qualsiasi cosa all'interno di una gamma veramente piccola di un numero intero venga arrotondata a quell'intero, ma devo aver scelto un intervallo troppo grande perché ho continuato a arrotondare falsamente altri numeri che non intendevo. – Adam

+0

'if (fabs (x - y) <0,000001)' dovrebbe fare il trucco. Sarà vero se x è quasi uguale a y (in questo caso y è 0). – AshleysBrain

+0

Questo è essenzialmente ciò che stavo cercando ... Ho fatto un tentativo su questo da solo, ma la mia costante di soglia era troppo lenta. Questo sembra funzionare abbastanza bene. – Adam

3

3.58979e -009 questo è 0,0000000358979

È un ~~ 0 come il tuo ~~ PI.

3

E 'uguale a zero se il vostro operatore di uguaglianza ha abbastanza tolleranza

-1

più cifre significative potrebbe aiutare. Il mio compilatore C (gcc) usa la costante 3.14159265358979323846 per M_PI in "math.h". Oltre a questo, non ci sono molte opzioni. Creare la tua funzione per convalidare la risposta (come descritto in un'altra risposta alla tua domanda) è probabilmente l'idea migliore.

+0

Perché Python è diverso da quello dopo il 15 ° decimale? –

+0

* shrug * Sto solo fornendo informazioni che ho. Sembra che Python sia impreciso ... Ora viene rimosso. : P – Dustin

+0

Sembra che tu stia usando un doppio per quello. Le cifre aggiuntive non saranno affatto d'aiuto. Le cifre corrette sono 3.141592653589793 2384626433 ... – EvilTeach

1

si potrebbe scrivere un po 'di funzione wrapper:

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

Come altri hanno notato, la matematica in virgola mobile è notoriamente inesatto. Hai bisogno di un qualche tipo di tolleranza se vuoi che qualcosa appaia esattamente allo zero.

+0

Potrebbe anche essere necessario un confronto anche per la versione 1.0. –

+0

E i valori che dovrebbero equivalere esattamente a 0,5 ecc.? – UncleBens

+0

Non otterrai mai esattamente ½√3, quindi alla fine dovrai ammettere le limitazioni del punto di virgola mobile. – MSalters

15

Mettiamola in questo modo, è 3.58979e-009il più vicino-0 come valore 3.14159265 è quello il vero Pi. Quello che hai è, tecnicamente, quello che hai chiesto. :)

Ora, se si inseriscono solo 9 cifre significative (8 posizioni decimali), quindi indicare all'output di non visualizzare più, ad es.uso:

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

Eh? È quasi esattamente alla stessa distanza (come ci si aspetterebbe dal teorema di Taylor). –

+0

@ Mike, era solo una parabola ... nel senso non matematico. :) OK, nell'interesse della scienza ora è corretto. – vladr

+0

che in realtà è quello che ho in questo momento, ma io continuo a ricevere -0,00 per certi ingressi e l'unico modo che posso pensare di aggirare -0,00 comporta l'uso di sprintf() e confrontandola con "-0.00" e hardcoding nel valore corretto. – Adam

1

perché non la forza per tuttavia molte cifre è necessario

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

sin (PI) deve essere uguale a 0, per un valore esatto di PI. Non stai inserendo il valore esatto di PI. Come altre persone fanno notare, il risultato che ottieni arrotondato a 7 cifre decimali è 0, il che è abbastanza buono per la tua approssimazione.

Se è necessario un comportamento diverso, è necessario scrivere la propria funzione seno.

+4

Per un valore esatto di PI e un'esatta implementazione del peccato. I computer binari non ne accolgono nessuno. –

0

Se si utilizza float o double in operazioni matematiche, non si otterranno risultati esatti. La ragione è che in un computer tutto è memorizzato come una potenza di 2. Questo non si traduce esattamente nel nostro sistema di numeri decimali. (Un esempio è che non c'è nessuna rappresentazione nella base 2 di 0.1)

Inoltre, float e double sono almeno 64 bit su alcuni compilatori e piattaforme. (Penso che qualcuno mi corregga se necessario). Ciò causerà alcuni errori di arrotondamento per valori molto grandi o per valori molto piccoli (0,0000000000xxx)

Per ottenere risultati esatti è necessario disporre di una grande libreria di interi.

Come scritto nei commenti alla domanda di cui sopra vedere il sito ... http://docs.sun.com/source/806-3568/ncg_goldberg.html

4

Hai provato M_PI, disponibile nella maggior parte <cmath> o <math.h> implementazioni?

Anche così, l'utilizzo del punto mobile in questo modo introduce sempre una certa quantità di errore.

+0

sì purtroppo cout << sin (M_PI) non è ancora pari a zero – Adam

+1

@ Adamo: è più vicino, però! – Potatoswatter

+5

bene sai cosa si dice: chiudi conta solo in ferri di cavallo e cifre decimali ... aspetta un secondo! – Adam

2

È possibile inserire un numero maggiore di cifre per ottenere un risultato migliore (provare ad esempio 3.1415926535897932384626433832795029L), ma si verificano comunque errori di arrotondamento.

Ancora, è possibile creare le proprie versioni sin e cos che confrontano il valore Pi noto e restituiscono esattamente zero in questi casi.

namespace TrigExt 
{ 
    const double PI = 3.14159265358979323846; 

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

È inoltre possibile espandere questa cosa per le altre funzioni trigonometriche e di gestire multipli Pi.

-1

Lo sai, solo per la correttezza matematica là fuori: sin (3.14159265) non zero. È approssimativamente zero, che è esattamente ciò che il programma ti sta dicendo. Per i calcoli, questo numero dovrebbe darti un buon risultato. Per la visualizzazione, fa schifo, quindi ogni volta che si stampa un float, assicurarsi di formattare il numero.

Non penso davvero che ci siano delle meccaniche fluttuanti nel lavoro qui ... è solo matematica semplice.

Tuttavia, per quanto riguarda il codice, fare attenzione ... non fa in modo che il codice fornisca il risultato errato facendo le approssimazioni prima del display, solo visualizzare le informazioni nel modo corretto.

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

questo azzererà i valori sotto la soglia, usare in questo modo cut(sin(Pi))

+0

una nota alla cautela: abs (double) fa ciò che è inteso in C++, ma non in C. –

3

Questo dovrebbe visualizzare lo zero:.

cout << fixed << sin(Pi); 

(non credo che si dovrebbe cercare di arrotondare nulla se siete preoccupati per la visualizzazione, occupatevi delle funzioni del display, non del valore stesso.)

+0

+1: questa è una delle poche risposte sulla pagina che produce effettivamente l'output desiderato. Unico problema è che devi aggiungere 0.0 a sin (Pi) per evitare di ottenere -0.0 come risultato in certi casi. Per questo motivo preferisco la risposta che ho selezionato per essere la "risposta accettata". – Adam