Newsgroup: it.comp.lang.visual-basic
Titolo: [!] ciclo For con valori frazionari: bug?

Salve,
forse ho solo scoperto l'acqua calda, ma recentemente mi sono accorto
di un comportamento di vb che mi ha lasciato un po' perplesso. Provate
ad eseguire questi cicli:
Dim i As Single
For i = 0.4 To 0.6 Step 0.1
Debug.Print i
Next i
Debug.Print i <= 0.6
For i = 0.6 To 0.8 Step 0.1
Debug.Print i
Next i
Debug.Print i <= 0.8
For i = 0.8 To 1 Step 0.1
Debug.Print i
Next i
Debug.Print i <= 1
Ora, nel primo caso va tutto bene; nel secondo già cominciano i
problemi perchè il ciclo viene eseguito solo due volte anzichè tre:
ciò è dovuto a imperfezioni nell'approssimazione dei valori frazionari.
Non è una buona scusa per un errore del genere, ma almeno vb è
"coerente", perchè all'uscita dal ciclo pensa che i>0.8, anche se non
dovrebbe esserlo. Ma il terzo caso è proprio assurdo: non solo il
ciclo è eseguito solo due volte, ma all'uscita risulta che i<=1:
MA ALLORA PERCHE' NON LO ESEGUE PER LA TERZA VOLTA!?!? Come cavolo
li fa i confronti, a casaccio?

 ha scritto nel messaggio
news:9nfr17$79dud$1...@ID-96152.news.dfncis.de...
è già un mezzo bug di per se utilizzare una variabile floating point per
controllare un ciclo for, a causa di limiti di precisione dovuti alle
approssimazioni dello stesso formato floating point (indipendente dal
linguaggio di programmazione, stesso discorso anche in C/C++, Delphi o
altro);

Forse non mi sono spiegato bene.
So benissimo che è poco saggio usare valori frazionari come
contatori nei cicli, e so anche che con tali valori ci possono
essere problemi di approssimazione (sono stato proprio io a
farlo notare). Quello che non capisco è perchè all'uscita dal
terzo ciclo risulti i<=1: se vb è convinto che valga questa
relazione, perchè non esegue il ciclo anche la terza volta?
Che io sappia, questo non succede col C. Non è un bug?
Giorgio


 Giorgio
Forse lo so io...
Il tuo numero single in realta', una volta convertito in double, risulta
 1,10000002384186
Questo e' dovuto alla cifra di guardia, lo stesso motivo per cui 1.08 single
una volta convertito in double fa
 1,08000004291534
In pratica il numero che usi per come base o come incremento e' un numero
periodico; quando VB fa il confronto senza arrotondamento, per il loop,
quella minuscola discrepanza e' sufficiente a fargli ritenere (giustamente)
superato il valore 1; quando pero' tu fai il confronto con 1 lui considera
la cifra di guardia finale, che gli fa arrotondare il risultato a 1
spaccato, con conseguente salto del ciclo.
Da cui il consiglio di usare quantomeno un intero scalato (currency) per i
tuoi calcoli, oppure di usare i long e riscalare al volo.
Ciaotutti,
*

  1,10000002384186
Aspetta, c'è qualcosa che non mi quadra: a me risulta:
cdbl(1.1!)=1,10000002384186
ma cdbl(1!)=1
In altre parole, quando il mio contatore arriva a 1 (quando dovrebbe fare il
terzo ciclo) risulta proprio 1, non 1 virgola qualcosa. Perciò continuo a
non capire perchè il ciclo si interrompe: a tutti gli effetti il mio
contatore assume il valore 1 tondo, senza cifra di guardia. Al valore 1.1
non ci arriva mai, è questo il problema.
Giorgio.


Ma il tuo codice non testa 1!, ma
' Codice
For i = 0.8 To 1 Step 0.1
    Debug.Print i
Next i
' Percui prova a usare questa linea:
Debug.Print CDbl(i)
' Fine Codice
Ciaotutti,
*

 Debug.Print CDbl(i)
Viene sempre 1 tondo.


Mmm... A questo punto io controllerei il decompilato, ma siccome non ho
tempo non lo faccio :P
Ciaotutti,
*


Pardon, prima che qualcuno si metta in teste idee strane: ovviamente volevo
dire il disassemblato.
Ciaotutti,
 *


il costrutto for() del C/C++ č qualcosa di sintatticamente molto piů potente
e flessibile del For di VB;
in ogni caso, ribadisco, non č _mai_ buona norma utilizzare un For in VB con
variabili in virgola mobile;
usa un Do / Loop;
ciao