Newsgroup: it.comp.lang.delphi
Titolo: Float Real + win9x LUNGO ma INTERESSANTE

Allora il discorso è un po' lungo :
D6 + MS SQL Server 2000 + Midware FPiette (ma quest'ultimo credo proprio non
abbia colpe)
Allora sul db avevo un campo di tipo float.
Dovete sapere che usando questo tipo di campi su SQL server si introducono
degli errori; per esempio, un utente inputa 198.5 e sul server trovavi
scritto 198.499999999.
Il problema nasceva in casi in cui io dovevo arrotondare questo valore
all'intero superiore; come facevo? Numero + 0.9 e ne prendevo la prte
intera!
Dopo aver notato la problematica intrinseca nel tipo di campo da me adottato
sono passato al tipo di campo numeric(scale 30 precision 10, cioè un numero
lungo in totale massimo 30 cifre di cui 10 decimali), questi essendo preciso
se l'utente digita 198.5 sul server scrive 198.5000000000...
Bene, mi dico, dovrei aver risolto; invece NO!
Allora il proble ma è questo :
- io sulla mia macchina Win2000
Campo sul server 141.1000000000
Campo ritornato dalla query letto con un DBedit 141.1
parte intera(campo + 0.9) = 142
- Miei utenti con win 95, 98 o Millenium
Campo sul server 141.1000000000
Campo ritornato dalla query letto con un DBedit 141.1
parte intera(campo + 0.9) = 141    <===== !!!!!!!!!!!!!!!!
Allora la varibile di appoggio che utilizzo per salvare il campo dopo averlo
letto dalla tabella era un REAL e con questo avevo il problema sopra
descritto, ho prpvato con EXTENDED e avevo lo stesso problema... poi con
SINGLE ...  TUTTO OK !!!!!!
Cavolo ragazzi, ma voi ne sapete niente?
Tutto questo mi uccide, il ns applicativo è pieno di REAL.... ma Win9x ha
problemi a trattare decimali oltre i 32 bit?
Ciao e grazie

 Ciao e grazie
Vuoi Risolvere? 
Non usare MAI campi di tipo Real. Per qualsiasi valore a virgola mobile
usa i campi di tipo Currency.
Non è un problema di DB o di come li salvi sul DB. 
Questo è dovuto ad un problema interno sulla gestione dei Floating
point.
I Currency sono anchessi campi a virgola mobile ma gestiti diversamente
a livello di processo.
Un consiglio? Rimpiazza tutti i tipi con Currency altrimenti non ne esci
vivo.
Acqua
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 intera!
Non mi sembra la cosa migliore...
Io avrei utilizzato il doppio cambio di segno (e comunque per un numero con
una sola cifra decimale, utilizzerei un intero e farei la divisione solo in
fase di visualizzazione o stampa)

Ok Alessandro,
però mi piacerebbe capire i motivi.
Ora avendo
REAL 8 byte (64bit)  (equivale a un  DOUBLE)
1  bit di segno11 bit di esponente52 bit di mantissaSINGLE 4 byte (32 bit)
1  bit di segno8  bit di esponente23 bit di mantissa
Capisco solo che
-se utilizzo il primo con Win2000 funziona e con Win9x no
-se utilizzo il secondo funziona sia con Win200 che con Win9x
quindi evinco che Win9x ha dei problemi nel trattamento dei float oltre i 32
bit (avendo peraltro provato anche con Extended)
Ma non sono del tutto convinto del tipo currency
Cutted from Delphi Help :
"Currency is a fixed-point data type that minimizes rounding errors in
monetary calculations. It is stored as a scaled 64-bit integer with the four
least-significant digits implicitly representing decimal places. When mixed
with other real types in assignments and expressions, Currency values are
automatically divided or multiplied by 10000"
Capisci? 64 bit! Non sono del tutto convinto che non avrò problemi.
Mi sai dare una spiegazione del tuo consiglio? Solo per capire, non mi piace
fare le cose senza capire, quindi se mi spieghi provvederò alle
sostituzioni.
Grazie ancora per la tua pazienza.

 fase di visualizzazione o stampa)
No, questo non è possibile con è solo un campo di I/O, è un campo frutto di
complessi calcoli che arriva in forma flottante, e che serve per altri
calcoli, in alcuni casi anche in virgola mobile.
Grazie per l'attenzione, spiegami però come funziona il doppio cambio di
segno ciao.

 intera!
perchè non usi la funzione ceil? ti ritorna il primo intero maggiore o
uguale a quello che gli passi:
ceil(9) -> 9
ceil(9.001) -> 10
ceil(9.9) -> 10
ceil (-1) -> -1
ceil (-4.4) -> -4

Capperonzolini !!!!
lo sai che la avevo già vista... E mi ero detto :
"Ceil, me la devo ricordare"
e adesso ecco Balbar che mi dice
"Shta fermo lì... che te lo dio il promemoria"
Grazie!!!! Sostituisco agilmente :))
Per il discorso dei flottanti e di Win9x hai qualche ideuzza?
Ciao!

 vivo.
Scusa Alessandro,
ma i campi Currency non hanno solo 4 decimali ? Almeno così è in D5.
Io con l'euro mi sono trovato nei casini con i Currency nei calcoli perchè
mi mancava il 5° decimale che è previsto per legge ( a volte anche 7 per
tipologie particolari) ed ho sostituito le variabili che uso nei calcoli da
Currency a real e la tipologia dei campi dei database (sempre nei calcoli)
da Currency a Float e così tutto è tornato a funzionare.
Io però parlo di files DBase o Paradox e non uso (haime) istruzioni SQL.
Ciao
--
MaoSw
Reporter Professional srl
support...@reporterprofessional.it

 però mi piacerebbe capire i motivi.
E fai bene a chiederli....
I Floating Point di tipo Single, Double, Extended possono avere problemi
per quanto riguarda l'arrotondamento e non soltanto (nel mio caso anche
in sottrazioni).
Esempio:
  1 / 3 = 0.3333 
di conseguenza 3 * 1/3 = 1
però se noi facciamo 
begin
  X := 1 / 3; 
  X := X * 3; 
  if X = 1 then // Questo non sarà vero!
  ...
  ...
end;
E perchè? Perchè X = 0.9999 e non 1.
Addirittura ricordo che in casi del tipo:
var
  Y, X, Z: Real;
begin
  X := 1.20;
  Y := 1.01;
  Z := X - Y; // Qui Z era uguale a 0.2 e non 0.19
end;
Semplicemente sostituendo Real con Currency tutto tornava giusto...
Non voglio dire una fesseria ma, se non erro, i tipi di dati Currency
inernamente sono rappresentati come degli interi lunghi
nei quali le ultime quattro cifre rappresentano la parte decimale....
In un articolo leggo:
Computers do not have infinite decimal places. 
The Float types do give you better accuracy as you go from Single to
Double to Extended but the above rounding errors persist.
So rather than do Equality comparisons, we need to define how close is
good enough. The following routines do this:
var 
  ESBTolerance: Extended = 0.00000001; // Set to the desired accuracy 
function SameFloat (const X1, X2: Extended): Boolean; 
begin 
  Result := abs (X1 - X2) < ESBTolerance 
end; 
function FloatIsZero (const X: Extended): Boolean; 
begin 
  Result := abs (X) < ESBTolerance 
end; 
function FloatIsPositive (const X: Extended): Boolean; 
begin 
  Result := (X >= ESBTolerance); 
end; 
function FloatIsNegative (const X: Extended): Boolean; 
begin 
  Result := (X <= -ESBTolerance); 
end; 


 da Currency a Float e così tutto è tornato a funzionare.
Non ho mai avuto la necessità di utilizzare numeri con + di 4 decimali
però ti
posso dire che con i Real ho avuto veramente "GROSSISSIMI" casini.
Non ricordo bene il problema avuto. Ma + o - era:
var
  X, Y, Z: Real;
begin
  X := 1.20;
  Y := 1.01;
  Z := X - Y; // Qui Z in realtà non mi dava 0.19 ma "0.2"
              // Ho avuto soltanto grossi casini con i Real...
              // Da allora uso soltanto currency. 
end; 
Sostituendo Real con Currency ... tutto ok. Ma non mi sono mai posto il
problema per quei valori con + di 4 decimali.
Qualcuno ha ulteriori info?



Quello che dici mi è tutto chiaro, solo che ancora non ho capito quali siano
le difficoltà che incontra Win9x lavorando con un Real (o Double) o un
Extended piuttosto che con un Single.
Farò delle prove, ma il fatto che il tipo currency sia a 64 bit così come il
Double mi fa insospettire riguardo al trattamento che potrebbe farne
Win9x...

problemi a trattare decimali oltre i 32 bit?
I numeri in virgola mobile sono imprecisi per natura.  Float, poi,
credo che abbia la precisione minore.  Il round-off è normale, in
questi casi.  Se i numeri da memorizzare sono importi, Currency è il
tipo adatto.  Contro l'imprecisione dei fp non c'è nulla da fare, se
non aumentare la precisione.
-- 
Andrea Laforgia - a.laforgia(at)
libero.it
"There are three kinds of people in this world; 
 those who are good at maths and those who aren't".

Capisci? 64 bit! Non sono del tutto convinto che non avrò problemi.
Non avrai problemi, il tipo Currency è studiato apposta.


141.0999999999999999999, capisci?
No.  Gli standard di memorizzazione dei floating point si basano tutti
sulla rappresentazione dei numeri come somme di potenze negative di 2.
Il numero 0.125 è rappresentabile sicuramente in modo esatto
(ln(0.125)/ln(2) = -3 ==> 0.125 = 2^-3), il numero 0.123 è invece già
più problematico (ln(0.123)/ln(2) = -3.023...).  In sostanza, il
numero finale è dato da una somma parziale di frazioni: 0.0625 +
0.03125 + 0.015625...
Più è alto il numero di frazioni coinvolgibili nella somma (i.e. più è
alta la precisione), minore è l'errore finale.
Con le regole di normalizzazione normalmente adottate nei fp, si
riesce a spingere la rappresentazione al massimo, ma c'è sempre un
limite oltre il quale si cade in forti imprecisioni (questo avviene
specialmente se si eseguono sottrazioni o divisioni tra numeri molto
vicini).


Currency a real
Sbagliatissimo.  I tipi a virgola mobile non vanno MAI usati per gli
importi in valuta.  Bisogna ricorrere ai tipi interi.


 serve per altri calcoli, in alcuni casi anche in virgola mobile.
Quindi non ha solo una cifra decimale, e potrebbe essere ad esempio 1.05? E
in questo caso, come fai ad arrotondarlo aggiungendo 0.9?


Scusa la mia profonda ignoranza,
questo significa che devo utilizzare che cosa ?
Ho già fatto molti programmi in D5 che utilizzano variabili real quando devo
fare dei calcoli con 5 o più decimali, dato che currency ne gestisce solo 4,
e probabilmente ho dei clienti che non hanno badato all'errore che
sicuramente si crea. Per il futuro, anzi rimetto volentieri a posto anche il
passato, vorrei evitare di continuare su questa strada.
Come mi consiglieresti di comportarmi  in presenza di variabili che
necessitano di gestire importi in valuta con 5 o più decimali ?
Grazie in anticipo per ogni tuo prezioso consiglio.


Ringrazio tutti per le delucidazioni sui tipi.
In quello che mi avete detto ci sono le stesse cose di cui ho parlato su
comp.database.ms-sqlserver e su it.comp.software.database quando avevo
perplessità sui campi float.
Non mi è ancora chiaro però il perché delle differenze di comportamento tra
Win9x e Win2000.
Avete qualche idea?
Grazie ancora dei preziosi consigli


Andrea ha perfettamente ragione. Lavoro ad applicazioni di calcolo
scientifico da anni e mi sbatto ogni santo giorno con i problemi legati ai
dati in virgola mobile.
Devi usare un tipo di dato a virgola fissa. Il Delphi non li supporta
nativamente, ma sono simulabili.
Devi innanzitutto decidere quante cifre riservare come decimali e quante te
ne servono a sinistra della virgola. Un intero a 64 bit puo' assumere un
valore massimo di:
+/-9223372036854775807
Se ti servono 7 cifre decimali, il numero massimo rappresentabile sara'
+/-922337203685
.4775807
che in eurovaluta sono 922 miliardi di euro.
Il Currency con 4 decimali arriva ad un massimo di 922 mila miliardi, ma
tenendo conto che prima veniva usato x le lire, credo che anche impostando 7
decimali tu possa gestire tranquillamente tutto.
Quando usi i valori nei calcoli non devi fare alcuna conversione. Dovrai
invece scriverti una eventuale MyCurrencyToString e viceversa, che tenga
conto della posizione del punto decimale.
I tuoi calcoli saranno sempre fatti quindi al decimilionesimo di euro (o di
unita' se non si tratta di valuta) e quella sara' la precisione massima.
Devi fare pero' molta attenzione alle moltiplicazioni, perche' con grosse
cifre il pericolo di overflow e' molto alto. Certo e' che, se si tratta di
valuta, le moltiplicazioni fra somme sono un'operazione inusuale (a meno che
non si tratti di debiti :P)
Ciao,
Stefano


Probabilmente impostano in modo diverso le modalita' di funzionamento del
coprocessore matematico
Ciao,
Stefano

 conto della posizione del punto decimale.
Cappero,
che strano però che Delphi abbia questa limitazione.
Comunque i 5  (o 7) decimali mi servono nei calcoli tipo
quantità*prezzo unitario=importo
e forse finora non mi sono accorto di nulla perchè poi l'importo lo
arrotondo a due decimali.
Grazie per la spiegazione



Non e' Delphi ad avere questa limitazione.
I virgola mobile sono tipi standard IEEE, gestiti direttamente dal
coprocessore. Delphi ne fornisce solamente una interfaccia
Ciao,
Stefano

 Ciao!
No, mi dispiace. Ma so che i numeri in virgola mobile danno spesso problemi
anche se non
pensavo fino a quel punto...