Newsgroup: it.comp.lang.visual-basic
Titolo: bella la virgola mobile...

snippet vb6/
vb.net
 
 
Dim s1 As Double
Dim s2 As Double
s1 = 8.45
s2 = 5.45
 
Dim s As Double
 
s = s1 - s2
's=3
MsgBox s
MsgBox Int(s)
MsgBox Fix(s)
 
snippet c#
 
double s1,s2;
 
s1 = 8.45;
s2 = 5.45;
double s;
 
s = s1 - s2;
//s=3
MessageBox.Show( s.ToString());
MessageBox.Show( Math.Floor(s).ToString());
MessageBox.Show(Math.Round(s, 0).ToString());
 
 
Provate a mettere .15 o .55 al posto di .45 oppure a scambiare il 
commento prima di s=3. Ovviamente deriva dalle proprietà del formato 
IEEE a virgola mobile, però se non è una fonte di grattacapi un 
comportamento simile..
 
fe...@delenda.net
 
-- 
http://delendanet.blogspot.com
 

Scriveva 
fe...@delenda.net
 venerdì, 28/12/2012:

Non è fonte di grattacapi, ma un'inevitabile 'stranezza' se si desidera 
utilizzare quel tipo di dato.
Nel momento in cui utilizziamo un tipo a virgola mobile, approfittiamo 
della possibilità di memorizzare numeri immensamente grandi o 
immensamente piccoli, tipici delle informazioni di tipo scientifico o 
di matematica finanziaria.
Ma rinunciamo alla visione decimale, tipica della nostra visione umana 
dei numeri.
Se si desidera congruenza fra il numero e la sua visualizzazione 
decimale, non si usano i tipi a virgola mobile bensì i tipi a virgola 
fissa, come Int, Money, ecc...
 
E' una scelta di progetto dello sviluppatore, che deve utilizzare il 
tipo adeguato alla logica applicativa.
 
E' evidente però che ogni volta che si passa da un virgola mobile ad un 
virgola fissa, si forza un mondo dentro un altro, e se questo avviene 
con elevata discontinuità (come con Int() o Fix()), la forzatura 
diventa macroscopica.
 
Ricordo una volta di avere postato un bug del sito Unicredit, che 
faceva la somma di alcuni effetti (in Euro, quindi al massimo 4 
decimali) utilizzando dei float, per cui il totale era qualcosa tipo 
12345,4356756456296547856948356
 
 
Ciao
 
Alberto
 
 

 
Beh, basta non usare campi a virgola mobile (es. Decimal) quando non si vuole un comportamento da virgola mobile...
 
Incidentalmente, i metodi di compatibilità VB6 Int() e Fix() mappano entrambi a Math.Floor()

 12345,4356756456296547856948356
 
Ho studiato meglio il mio caso ed è 'un tantinello' patologico in realtà.
 
Il codice che ho mostrato nasce dal debug di un programmino di 
importazione dati che mi è passato tra le mani e usava i double in 
partenza: prende delle timbrature da un file .csv e lo passa a un 
database creando degli INSERT sql al volo. Il problema che ho dovuto 
indagare è che funzionava benissimo, a parte per le ore con i 'quarti 
d'ora'. La cosa sconcertante è che se si stampa, in debug o meno il 
valore di s = 8.45 - 5.45, con s di tipo double, si visualizza 3, e non 
2.9999999999999999999.
Anche facendo qualcosa tipo 
String.Format({0:F0.000000000000000000000000000000000000000000000000000000000},s)
o 
Format(s,"0.0000000000000000000000000000000000000000000000000000000000000")
si visualizza sempre 3.00000..
 
Però Int(s)/Math.Floor(s) = 2, e anche (s == 3) equivale a false.
 
Ho tirato fuori la rappresentazione interna in IEEE 754 di 8.45-5.45
 
la mantissa è  + 3377699720527871, l'esponente è -50
 
Questo equivale, esattamente, a 3377699720527871/1125899906842624.
 
Ora, con 15 cifre decimali di precisione questo numero è:
 
N[3377699720527871/1125899906842624, 15] = 3.00000000000000
 
con 16...
 
N[3377699720527871/1125899906842624, 16] = 2.999999999999999
 
I double IEEE 754 supportano fino a 15 cifre decimali, evidentemente le 
routine di conversione in stringa si fermano alle 15 cifre, Math.Floor 
funziona diversamente. Inoltre se assegno double s = 3 il formato 
interno risultante è un c.d. numero 'subnormale' nel gergo dello 
standard (mantissa 3 ed esponente per convenzione a zero)
Questa precisione 'al limite delle 15 cifre' si verifica solo con certi 
valori delle ore/minuti espresse con un numero del tipo una e un quarto 
= 1.15 da cui il problema 'solo coi quarti d'ora'.
 
 
 
 
 
fe...@delenda.net
 
-- 
http://delendanet.blogspot.com
 

 1.15 da cui il problema 'solo coi quarti d'ora'.
 
Appunto, il concetto è proprio che è sbagliato inserire una lettura da 
un file csv, che per sua natura ha una precisione massima prefissata 
nelle espressioni numeriche, in un double.
Andrebbe preso a pedate nel culo chi ha scelto un double per metterci 
un'orario. Come capacità e precisione, il Money sarebbe stato perfetto.
A meno che, ma non credo sia il caso, il marcatempo non indichi anche i 
micromillesimi di secondo e la differenza di tempo lavorato in termini 
di micromillesimo di secondo non venga considerato in maniera utile.
E questo ci riporterebbe a quanto detto in precedenza, ossia che i 
virgola mobile hanno un senso per esprimere e gestire l'immensamente 
grande o l'immensamente piccolo. Il fatto, ad esempio, che le date 
vengano memorizzate di fatto in un virgola mobile, è proprio legato al 
fatto che lo stesso tipo di dato deve poter gestire la memorizzazione 
di due date distanti fra loro alcuni millenni, oppure di due date 
distanti fra loro pochi millesimi di secondo. La flessibilità in questo 
senso ce l'ha solo il virgola mobile.
E questo è il motivo per cui MAI e POI MAI si deve fare la differenza 
di due date con la sottrazione. A meno che non si debbano compiere 
particolari operazioni scientifiche che ci richiedano di oltrepassare i 
limiti della rappresentabilità umana.
Le differenze fra date devono essere sempre fatte usando datediff, che 
è la funzione che appunto considera le due date come tali e non come 
virgola mobile, e ne effettua la differenza secondo le leggi 
matematiche che regolano l'espressione del tempo.
 
Alberto