Newsgroup: it.comp.lang.c++
Titolo: Cast a int di un'espressione float

Qualcuno saprebbe spiegarmi perchè la seguente istruzione assegna 0 invece
che 1 alla variabile iVal?
int iVal = (int)(0.01f*100.0f);
Non dovrebbe valutare prima l'espressione (prodotto tra due float che
restituisce un float) e poi castare il tutto a int?? Le mie certezze stanno
crollando.........
Diversamente le istruzioni seguenti assegnano 1 alla stessa variabile iVal,
come mi aspetto che sia:
float fVal1 = 0.01f*100.0f;
int iVal1 = (int)(fVal1);
Sono confuso, nonostante la semantica dei due esempi di codice sembra essere
la stessa, il risultato è diverso!!
Ciao

 int iVal1 = (int)(fVal1);
A me stampa due volte 1, comunque la spiegazione, che
dipende anche da quello che fa il compilatore,
dovrebbe/potrebbe essere questa:
Se hai studiato un po' di rappresentazione dei
numeri floating point in base 2,
( se no vedi 
http://docs.sun.com/db/doc/800-7895
 )
dovresti sapere che il numero 0.01 non puo' essere
rappresentato in base 2 in modo esatto. Di fatto
corrisponde ad un numero periodico. Quindi in
realta' il valore che viene effettivamente
memorizzato e' probabilmente qualcosa del tipo
0.009999999999999999999999993453453245
Quando questo valore viene moltiplicato per 100
si ottiene qualcosa del tipo
0.9999999999999999999999993453453245
che viene successivamente troncato a zero nella
conversione double->int.
Il fatto che il mio compilatore mi stampi invece 1 puo'
dipendere da diversi fattori. Per esempio, (sto solo
cercando di indovinare) potrebbe essere che il calcolo
0.01*100 viene fatto a priori e viene memorizzata una
approssimazione calcolata in un altro modo, che magari
invece di 0.99999999999999999934535345354... fornisce
1.00000000000000000000000000256585685 e quindi viene
poi portato ad 1.
Ma torniamo al tuo esempio. Dici che il secondo
valore ti viene 1.
Questo e' ancora piu' ragionevole.
Il calcolo 0.01f*100f viene effettuato di default (se non vado errato)
usando dei double.
(in effetti l'uso dei float lo sconsiglio sempre a meno che
uno non sappia benissimo cosa sta facendo).
Poi pero' tu lo memorizzi in un float.
Facendo il cast da double (implicito) a variabile float, ovviamente
si perde precisione. Il compilatore deve effettuare una
approssimazione ed e' probabile che "tagliando" e approssimando
un valore double che magari corrisponde a
0.9999999999999999999999999997678678678 lui lo "approssimi"
proprio col valore 1.0. A questo punto la successiva conversione
in int ti fornisce un valore 1.
La morale e' che bisogna stare molto attenti quando si passa
da  valori in floating point a valori interi.
ciao,
g.

"Giovanni Resta" <g.r...@iit.cnr.it.nospam> ha scritto nel messaggio
news:c1mu10$c90$1...@newsfeed.cineca.it...

Ok, grazie per le tue preziose considerazioni, che mi hanno aiutato a capire
il problema.
Ho fatto altri test, e volevo aggiungere alcune considerazioni.
1. Innanzitutto, visto che nei tuoi test hai ottenuto in entrambi i casi il
risultato 1, ne desumo che il comportamento è diverso da un compilatore
all'altro.
Io ho usato il compilatore di Microsoft Visual Studio 6, appena avrò un
attimo di tempo proverò la stessa cosa con gcc su Linux. Una curiosità, lo
stesso codice in C#, e compilato con il Visual Studio 7 ha lo stesso
identico comportamento sopra riportato.
Mi chiedo se il differente risultato possa dipendere, oltre che dal
compilatore, anche dal processore su cui viene fatto girare il codice
macchina prodotto.
2. La tua spigezione sulla differenza tra i due esempi l'ho verificata, in
effetti è proprio come ipotizzavi.
Nel primo caso
float fVal1 = 0.01f*100.0f;    ---> fVal1 contiene 1.0
int iVal1 = (int)(fVal1);    ---> iVal1 contiene 1
 ho una conversione double--->float--->int,
 mentre nel secondo caso
int iVal2 = (int)(0.01f*100.0f);    ---> iVal2 contiene 0
ho una conversione double--->int, quindi senza il passaggio intermedio a
float.
Nonostante fossi a conoscenza che numeri decimali in binario non possono
essere rappresentati in modo esatto,
la cosa che in realtà mi sfuggiva è che il risultato di un'operazione tra
due float restituisce un double e che quindi, l'istruzione
float fVal1 = 0.01f*100.0f;
in realtà contiene una conversione implicita da double a float (errore da
pivello?... o linguaggio fuorviante?)
L'esempio seguente mostra più chiaramente quale sia l'effetto della
conversione implicita double--->float
float fVal1 = 0.01f*100.0f;    ---> fVal1 contiene 1.0
double dVal1 = 0.01f*100.0f;    ---> dVal1 contiene 0.99999997764825821
Ammesso questo (ammessa la mia ignoranza) sinceramente mi resta difficile
capire perchè la conversione double--->float approssimi al valore più vicino
(0.99999997764825821 diventa 1.0), mentre la conversione double--->int
tronchi il valore senza approssimarlo (0.99999997764825821 diventa 1.0).
Secondo me è fuorviante, per lo meno mi aspetterei lo stesso comportamento
in entrambi i casi (ancora lo zio Bill??). Lo dimostra il fatto che il
compilatore da te usato (qual'è?) ha un comportamento più coerente.
Ciao e grazie ancora