Newsgroup: it.comp.lang.c++
Titolo: Problema

Dopo 8 ore passate davanti al monitor non sono riuscito a risolvere questo
problema, probabilmente devo essere allo scuro di qualche particolarita' del
c++. Prendete queste 5 righe di codice, quanto vale d?
            double a,b,c,d;
            a=-1;
            b=-0.2;
            c=-5;
            d=a+(b*c);
Si fa anche a mente, il risultato e 0, ma con tutti i compilatori ( x DOS)
che ho provato fino ad ora ottengo 5.5112e-17, perché?
Non ci sono mica errori di arrotondamento. La cosa strana e che la
moltiplicazione viene fatta bene e da come risultato 1, poi quando basta
fare -1+1 viene fuori l'incredibile.
Aspetto qualche aiuto, perché da solo non riesco ad arrivarci.
Ciao e grazie.
Filo

 che ho provato fino ad ora ottengo 5.5112e-17, perché?
beh, 5.5112e-17 e' abbastanza piccolo per essere "quasi" zero...:)
Il problema e' che stai usando double, mentre 0 senza ulteriori cifre e' un int.
Se tu volessi ottenere un int, dovresti trasformare il double in int con un cast
e allora si che ti verrebbe 0. Non e' assicurato da nessuna parte che il
compilatore capisca da solo che il risultato di una espressione sia zero.


 Non ci sono mica errori di arrotondamento.
Invece dipende proprio da errori di arrotondamento. Anzi, più
precisamente, da errori di rappresentazione dei numeri floating-point.
EMG

fare -1+1 viene fuori l'incredibile.
Come hai fatto a vedere che la moltiplicazione ha come risultato 1?
Non avrai implicitamente eseguito una conversione a int?
Paolo
--------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
--------------------------------------------------

 compilatore capisca da solo che il risultato di una espressione sia zero.
Non posso convertire il double in un int, perché il mio vero scopo è di fare
centinaia di calcoli su una matrice di 1.000.000 di elementi tutti in
virgola mobile e quello zero che non è zero mi fa saltare tutto.
Rudy & Filo

 rimando ad un qualsiasi testo di calcolo numerico.
Ho già sostenuto l'esame di calcolo numerico e penso che 0.2 sia un numero
finito, rientrante senza problemi nei limiti della precisione macchina.
Infatti 0.2 è un numero razionale ( 1/5) non reale. Non c'è propagazione
dell'errore in quanto non c'è errore, il risultato della moltiplicazione è
corretto, lo vedo col debugger il quale mi rappresenta i numeri come sono e
mi dice giustamente che -0.2 * -5 da come risultato 1, se non fosse 1, ma
quasi 1 lo vedrei.
Il vero algoritmo che sto implementando fa centinaia di calcoli il floating
point (è l'algoritmo del simplesso per ricerca operativa) e per alcuni dati
in ingresso tutto fila liscio, per altri succedono queste cose.
Il pascal ad esempio questo errore non me lo da, come neanche la
calcolatrice.


Come mai allora il Pascal o anche la semplice calcolatrice fanno i calcoli
bene? Gli errori di arrotondamento si hanno prevalentemente con i numeri
reali, non certo con un numeri finiti quale 0.2.
Rudy & Filo

calcolatrice.
Non e` mica detto che utilizzino la stessa rappresentazione, anche la
mia calcolatrice apparentemente non riporta errori, ma perche` la
sua precisione e` limitata a sole 12 cifre...


fare -1+1 viene fuori l'incredibile.
Il problema e' dovuto al fatto che il risultato 1 della
moltiplicazione ti *appare* esatto, ma non lo e'. Probabilmente se
esamini il numero ti accorgi che risulta qualcosa del tipo
1.000000000000000055112 e quindi -1 + 1.000000000000000055112 ti da'
come risultato 0.000000000000000055112 che non e' *esattamente* 0.
--
Andrea Laforgia (
andrea_...@bigfoot.com
)
---------------------------------------------

 virgola mobile e quello zero che non è zero mi fa saltare tutto.
Ma quello zero E' zero, nei limiti della precisione del formato
floating-point. Diventa qualcosa di diverso solo se tenti di
confrontarlo in modo improprio con altri valori.
EMG

 finito, rientrante senza problemi nei limiti della precisione macchina.
Non è vero. Se provi a moltiplicare 0.2 per 2 tante volte ti accorgi che
non otterrai mai un numero intero. Questo significa che 0.2 non è
rappresentabile in binario con un numero finito di cifre, come invece lo
è, ad esempio, 0.25. Ecco il perché dell'errore nell'operazione.
EMG

 reali, non certo con un numeri finiti quale 0.2.
Come spiegato nel messaggio precedente, 0.2 è finito in base 10 ma
periodico illimitato in base 2.
EMG

> precisamente, da errori di rappresentazione dei numeri floating-point.
Concordo con EMG.
Il problema è dovuto alla rappresentazione (binaria) di un double. In
particolare, è il segno "meno" a causarti i grattacapi. I numeri floating point
negativi vengono solitamente rappresentati in complemento a 2 e questo è causa
di difetti d'approssimazione. Se tu avessi valori positivi per tutte e tre le
variabili a, b e c, otterresti 2 tondo tondo, come deve essere.
Ciao
AB
--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Andrea Baruzzo
Via Circonvallazione, 26
Muzzana del Turgnano (UD) - Italy
e-mail: 
andrea....@ud.nettuno.it
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Fatti queste domande e datti una risposta. (e' solo per rendere tutto un po'
marzulliano: lungi da me ogni arroganza!)
Com'è la rappresentazione in base 2 del numero 1/5 ?
Com'è la rappresentazione in base tre del numero 1/3 ?
Com'è la rappresentazione in base (a*b*c) del numero 1/(b*c) ?
Com'è la rappresentazione in base (a*b) del numero 1/d ?
E concluderai (oltre ad altre cose) che il numero di cifre necessario a
rappresentare un numero dipende dalla base utilizzata.
Da questo punto di vista non conta se il numero sia trascendente o razionale
o che.
1/3 non è rappresentabile con un numero finito di cifre in base 10 e si
ricorre alla notazione 0.3 col 3 periodico.
1/3 è benissimo rappresentabile in base 3 e si scrive 0.1
La rappresentazione floating point è in base 2 con matissa di n bit,
esponente polarizzato a......... la rava la fava
Base 2.
---
Livio Cavallo

calcolatrice.
Non e` mica detto che utilizzino la stessa rappresentazione...


come risultato 0.000000000000000055112 che non e' *esattamente* 0.
Non e` esattamente cosi`, l'errore finale e` determinato oltre che da un
errore di rappresentazione (inerente) di -0.2, dall'errore algoritmico 
della funzione calcolata.
L'errore finale e` costituito da 3 componenti:
a) errore inerente (l'errore di approssimazione sui dati iniziali)
b) errore analitico (dovuto all'approssimazione di un modello matematico
     continuo con uno discreto)
c) errore algoritmico (dovuto all'uso di una aritmetica approssimata).
Trascurando b) che dipende da fattori esterni non valutabili a priori,
l'errore finale e` dato a dalla somma di quelli ai punti a) e c).
In particolare c) e` funzione dell'errore inerente e di un fattore
che dipende dalla funzione calcolata e dal punto in cui la si calcola
secondo la formula
Sommatoria_su_i ( err_iner( x(i) ) * |x(i) * f'( x(i) )| / | f(x(i) | )
con i che varia da 1 a n, n arieta` di f, x(i) i-esimo argomento
della funzione, f' derivata parziale di f in x(i).
A rigore andrebbero aggiunti errori di ordine inferiore, che pero` in 
quanto tali vengono solitamente trascurati.


variabili a, b e c, otterresti 2 tondo tondo, come deve essere.
Questo non mi risulta, i valori in virgola mobile che io sappia vengono
sempre rappresentati in segno, mantissa, ed esponente (+ una costante
che rende l'esponente sempre positivo), almeno sui pc.
Tant'e` che anche operando come tu dici io continuo ad ottenere sempre
lo stesso errore inerente (cambia solo il segno) e lo stesso errore
finale (in effetti c'e` una lieve differenza dovuta all'arrotondamento, 
ma si tratta di 6e-17 contro 5.5e-17).
Usando long double il mio debugger mi da:
0.2                -->    .200000000000000011
0.2*5.0         -->  1.00000000000000006
1.0 + .2*5.0 -->   2.00000000000000006


1.0 + .2*5.0 -->   2.00000000000000006
I risultati che io ottengo, invece, sono questi:
0.2 ------------------> 0.20000000000000
0.2*5.0 -------------> 1.0000000000000
1.0 + .2*5.0 -------> 2.0000000000000
mentre, per i valori con segno negativo, ottenevo:
-.02 ---------------------------> -0.20000000000000
(-0.2)*(-5.0) -----------------> 1.0000000000000
(-1.0) + ((-0.2 )* (-5.0)) ---> 5.5511151231258e-017
La documentazione fornita col compilatore (MS VC++ 6.0) precisa:
-----------------------------------------------------------------------------
Type-Name   Bytes   Other Names     Range of Values
double             8           none          1.7E +/- 308 (15 digits)
long double    10           none         1.2E +/- 4932 (19 digits)
precisando, inoltre:
The long double data type (80-bit, 10-byte precision) is mapped directly to
double (64-bit, 8- byte precision) in Windows NT and Windows 95.
--------------------------------------------------------------------------------
Preciso che la macchina su cui l'ho provato è un pc con processore cyrix MII 300
MHz e Windows NT 4.0 sp 4.
Insomma, oltre le 15 cifre di precisione non c'è da giurarci sull'esatteza del
risultato riportato, e questo spiegherebbe la discrepanza dei risultati. E'
strano che il debugger riesca a riportare un 5.5511151231258e-017 nel caso di
valori negativi e approssimi invece a 2 per quelli positivi (forse quest'aspetto
mi ha fatto cadere grossolanamente in errore, nel post precedente). Forse,
proprio perché oltre quella precisione nulla è garantito, tutto è possibile...
(o sono di nuovo in errore io nell'interpretare i risultati?)


(o sono di nuovo in errore io nell'interpretare i risultati?)
Io ritengo strano il comportamento di VC++ relativamente a come si
comporta con long double.


comporta con long double.
Anch'io, se è per quello. Tutto sta (forse) nel lapidario commento sul mapping
da long a double che, comunque, è un dettaglio di implementazione che riguarda
solo Windows 95 ed NT. Chissà, su altre piattaforme forse ciò non accade. A
proposito, ma il VC++ lo fanno ancora per il Mac?


proposito, ma il VC++ lo fanno ancora per il Mac?
Onestamente non saprei, e sempre onestamente io vedo
i mac rilegati ad una ristretta nicchia ecologica a me scarsamente
interessante (ma se mi pagano bene potrei ricredermi :).


 l'introduzione di un temporaneo (e).
Il problema, secondo me, è che se il risultato è del tipo
2.000000000000000x
allora la visualizzazione sarà 2 e sembrerà corretta mentre se abbiamo
1.999999999999999x
allora, pur avendo un errore effettivo analogo ma nel verso opposto, il
numero risulterà visualizzato come 1.999999999999999 sembrando
scorretto.
EMG

 viene rimappato su un double con solo 8 byte di precisione. Vi pare
sensata?
http://info.rutgers.edu/Techdir/lang-5.0/DOC5.0/lib/locale/C/html/common/ug/
goldberg1.doc.html
rappresenta, come dice il titolo, quello che OGNI programmatore dovrebbe
sapere sull'aritmetica floating point.  Dopo averlo letto e meditato, non
sara`
piu` causa di sorpresa che l'approccio "extended" al FP *non* sia affatto
una
panacea, anzi.
Lettura consigliata anche:
http://www.sns.ias.edu/Main/computing/compilers_html/common-tools/numerical_
comp_guide/index.html
Alex