2010-05-07 6 views
7

scusate se stupido ma non è stato possibile trovare una risposta.Errore di troncamento numerico C++

#include <iostream> 

using namespace std; 

int main() 
{ 
double a(0); 
double b(0.001); 
cout << a - 0.0 << endl; 
for (;a<1.0;a+=b); 
cout << a - 1.0 << endl; 
for (;a<10.0;a+=b); 
cout << a - 10.0 << endl; 
cout << a - 10.0-b << endl; 
return 0; 
} 

uscita:
6.66134e-16
0,001
-1.03583e-13

Provato compilarlo con MSVC9, MSVC10, Borland C++ 2010. Tutti loro arrivare in termina con l'errore di circa 1e-13. È normale che si verifichi un accumulo di errore così significativo con incrementi di 1000, 10000?

+3

http://docs.sun.com/source/806-3568/ncg_goldberg.html – Anycorn

+0

http://home.comcast.net/~tom_forsyth/blog. wiki.html # [[A% 20matter% 20of% 20precision]] (non io, heh) –

risposta

13

Sì, questo è normale errore di virgola mobile di rappresentazione numerica. Ha a che fare con il fatto che l'hardware deve approssimare la maggior parte dei numeri in virgola mobile, piuttosto che memorizzarli esattamente. Quindi, il compilatore che usi non dovrebbe avere importanza.

What Every Computer Scientist Should Know About Floating-Point Arithmetic

+0

Grazie, capisco errore di troncamento e tutte le altre schifezze. Ho fatto un sacco di lavoro teorico sui metodi numerici ma non l'ho mai controllato personalmente e sono rimasto molto sorpreso di scoprire quanto sia grande ... – Andrew

+1

Bene, un doppio ti dà all'incirca 16 cifre decimali di precisione, che è giusto. Quando esegui il loop 1000 volte, sei fino a 13 cifre di precisione. – WhirlWind

+1

Ricorda che è particolarmente doloroso solo con numeri irrazionali (in base 2). Se usi i poteri di due, starai bene. Per esempio. se il sommatore era 0,5, 0,25, 0,125, 0,0625, ecc., alla fine si sommano * esattamente * a 1,0, poiché questi valori nella base 2 sono 0,1, 0,01, 0,001 e 0,0001. –

1

Questo è il problema con i numeri in virgola mobile — sono approssimativi, e le cose strane accadono a zero (cioè, appaiono rappresentazioni strano). Per questo motivo, alcune delle operazioni sui numeri che date per scontate devono essere gestite in modo più delicato.

Quando si confrontano due numeri, non si può semplicemente dire a == b perché uno potrebbe essere 0 e l'altro -1.03583e-13 a causa della perdita di precisione lungo le operazioni in virgola mobile applicato per arrivare al a e b. Devi scegliere una tolleranza arbitraria, come questa: fabs(a,b) < 1e-8.

Quando si stampa un numero, è spesso necessario limitare il numero di cifre stampate. Se si utilizza printf, è possibile pronunciare printf("%g\n", a);, che non stampa cose come -1.03583e-13. Non so se c'è un analogico iostream su %g; è lì?

2

Questo è il motivo per quando si utilizza un errore di floating point non si dovrebbe mai fare:

if(foo == 0.0){ 
    //code here 
} 

e invece fanno

bool checkFloat(float _input, float _compare, float _epsilon){ 
    return (_input + _epsilon > _compare) && (_input - _epsilon < _compare); 
} 
+1

Controllare su zero va bene in molte circostanze in quanto può essere rappresentato esattamente nell'hardware, ma ovviamente dipende da cosa stai facendo con esso ... Partendo da 1 e sottraendo .1 dieci volte non porterai a 0, ovviamente. –

+1

Questo è molto vero ma nella maggior parte dei casi non è solo una buona pratica. Mi piace sbagliare sul lato della cautela. – wheaties

+0

errando sul lato della cautela ... divertente;) O forse errando sotto le dimensioni di epsilon. – WhirlWind

2

pensare a questo. ogni operazione introduce un leggero errore, ma l'operazione successiva utilizza un risultato leggermente errato. dato abbastanza iterazioni, si devierà dal vero risultato. se vuoi, scrivi le tue espressioni nel modulo t0 = (t + y + e), t1 = (t0 + y +e) e trova i termini con epsilon. dai loro termini è possibile stimare l'errore approssimativo.

c'è anche una seconda fonte di errore: ad un certo punto si stanno combinando numeri relativamente piccoli e relativamente grandi, verso la fine. se si richiama la definizione della precisione della macchina, 1 + e = 1, a un certo punto le operazioni perdono bit significativi.

Speriamo che questo aiuta a chiarire in termini laici