Newsgroup: it.scienza.informatica
Titolo: errore nella somma algebrica

In un programma in visual basic (VBA) ho trovato degli errori nella somma algebrica usando numeri "piccolissimi".
se scrivi:
50# * 0.2# - 10 il risultato non è zero ma un numero decimale.
lo # è il segno dei double (il numero viene rappresentato come un double)
se invece si fa:
x=50#*0.2#
x-10 torna 0
(50#*0.2#)-10 sbaglia
int(50#*0.2#)-10 è 0
anche se si mette un altro convertitore di formato al posto di int() il risultato è zero.
a cosa è dovuto l'errore, come si evitano errori del genere e dove trovo documentazione specifica?

 trovo documentazione specifica?
http://docs-pdf.sun.com/800-7895/800-7895.pdf
-- 
Uno studente durante il corso di filosofia di Morris Cohen chiese al
professore: "Come faccio a sapere di esistere?".
Il professor Cohen rispose: "Chi sta facendo questa domanda?".

 documentazione specifica?
Nessun errore. I numeri nel calcolatore sono tipicamente
memorizzati in binario. Se converti 0.2(decimale) nel
suo corrispettivo binario ottieni un numero periodico,
tipo  0.00110011001100110011001100.....
Questo numero periodico ovviamenete non puo' essere
memorizzato in modo esatto in un double, che puo'
contenere un numero finito di bit (tipicamente 64 bit,
che contengono oltre alla mantissa anche l'esponente e il segno).
come viene "tagliato" il numero puo' dipendere da diverse cose.
per esempio dovendo tagliare il numero
0.0011001100x1100... nel punto x  potrebbe venire approssimato come
0.0011001101   usando quindi un numero leggermente superiore a 0.2
mentre se venisse tagliato nel punto y:
0.0011001100110y011....
0.0011001100110
avremmo un numero leggermente inferiore a 0.2
Una volta che la conversione da decimale a binario ha introdotto un 
qualche errore tutte le operazioni che fai su quel numero
perpetueranno, o spesso amplificheranno l'errore iniziale
(detto "errore di rappresentazione").
A questo punto devi poi considerare come vengono convertiti
i double in interi.
In molti linguaggi la conversione avviene per troncamento.
Quindi se il tuo numero double e'
10.000000000000000000000154252 l'intero corrispondente sara' 10
mentre se il tuo double e'
9.9999999999999999999999335235 l'intero corrispondente sara' 9.
La stampa di un double invece potrebbe includere
un forma automatica di arrotondamento, e quindi, stampando
con 5 cifre decimali esatte sia 10.0000000003453454 che
9.9999999999235235235 otterresti comunque
10.00000
Il modo migliore per affrontare questi problemi e' la conoscenza.
Conoscenza della codifica binaria e della natura delle operazioni.
Nel caso sopra, per ottenere comunque 10, anche nel caso
che il double sia lievemente inferiore a 10, basta sommare una
piccola costante, tipo 0.0000000001 che portera'
il 9.999999... oltre il 10, ma non portera'
il 10.0000000022..... oltre l'11 quindi una volta troncato dara' 
l'intero giusto.
I vari linguaggi di programmazione contemplano poi di solito
delle funzioni di arrotondamento ( tipo round() ) che permettono
di automatizzare questo procedimento.
In generale devi
1) stare molto attento quando ti aspetti un risultato intero
partendo da operandi in floatingpoint, perche' non e' detto
che il risultato sia quello che ti aspetti.
2) non basare il tuo programma su confronti tra double.
due valori double che matematicamente sono lo stesso valore ma
sono ottenuti in modo diverso molto spesso sono diversi
(magari differiscono per un solo bit finale, ma questo e'
bastevole a far fallire un confronto di ugualglianza.)
per esempio se x e y sono due numeri in floating point
tipo  x=0.434534 e y=0.22211 allora
(x+y)*(x+y)  e x*x+y*y+2*x*y possono benissimo essere due
numeri double distinti, anche se molto vicini.
Per questo motivo, a meno che uno non sappia esattamente
cosa sta facendo, i numeri double andrebbero confontati
non con l'operatore di uguaglianza (a==b oppure a=b, non ricordo
il basic) ma con qualcosa del tipo
abs(a-b) < eps
dove abs() calcola il valore assoluto e eps e' una costante
opportunamente piccola.
Una buona trattazione dei numeri floating point nel calcolatore la
trovi qui:
http://www.physics.ohio-state.edu/~dws/grouplinks/floating_point_math.pdf
ciao,
g.

 50# * 0.2# - 10 il risultato non è zero ma un numero decimale.
Scommetto che il numero è prossimo a 0.
Conosci la differenza tra numeri reali e floating point?
Lo sai come viene rappresentato 0.2 in binario?
Prendi le potenze di 2:
16, 8, 4, 2, 1, 1/2, 1/4, 1/8, 1/16, 1/32, 1/64, 1/128, ....
e prova ad ottenere 0.2 con una somma finita di tali potenze.
1/8 + 1/16 + 1/128 + 1/256 + 1/2048 + ...
Pensi di riuscire a rappresentare 0.2 in questo modo?
No, prima o poi dovrai fermarti e, per quanto piccolo sarà l'errore 
real(0.2) - floating(0.2), non varrà 0.
Se t'interessa l'argomento, studia "Calcolo Numerico" o per lo meno 
cerca informazioni sulla "propagazione degli errori".
Per es. esistono situazioni nelle quali la soluzione attesa è 1 e quella 
calcolata 1000000000000. Come?
Semplice; pensa al tuo esempio: cosa succederebbe se l'operazione 
successiva fosse un prodotto con un numero altissimo?
Il risultato reale dovrebbe essere 0, ma otterresti qualcosa come 
16098345349034.
Kiuhnm