Newsgroup: it.comp.lang.c
Titolo: Non capisco l'operatore di casting

Ciao,
scusatemi se la mia domanda e' ingenua ma non sono un informatico.
Stavo esaminando (per motivi didattici) la questione della interazione
tra un linguaggio ad alto livello come il C e il motore floating point
sottostante.
Come riferimento, guardate
http://cch.loria.fr/documentation/IEEE754/ACM/addendum.html
Nell'architettura x86, tutti i calcoli intermedi vengono fatti
in precisione estesa (80 bit) e questo spiega il comportamento
del seguente programma (compilato con gcc su linux):
main() {
   double a,b,c;
   a = 1;
   b = 3;
   c = a/b;
   if (c == a/b) {
     printf("Equal\n");
   } else {
     printf("Not Equal\n");
   }
}
che ha come output "Not Equal" perche' nell'if la quantita' a/b e'
calcolata in precisione estesa e quindi non e' uguale a c che e' un
double.
So far so good. Quello che non capisco e' perche' sostituendo la riga
   if (c == a/b) {
con la riga
   if (c == (double) (a/b)) {
il risultato continua ad essere "Not Equal". Ho controllato i due
eseguibili in assembler e sono *identici*. Ma allora cosa fa
precisamente l'operatore di casting?
Grazie a tutti,
Ale
--
russo at matapp dot unimib dot it

 precisamente l'operatore di casting?
<cut>
a,b,c, sono gia' double quindi in cosa dovrebbe modificarli il 
compilatore con il type casting?
Il type casting serve a cambiare il cast di una variabile tra due tipi 
diversi ad esempio tra un int e un long etc.etc.
Ciao
Giuseppe

 -O2 ottengo equal sia nel primo sia nel secondo caso. 
In questo caso il compilatore riconosce "in astratto" che a/b
e' uguale a c, e quindi funziona indipendentemente dal casting
perche' fa un'ottimizzazione. Prova a sostituire la riga
   c = a/b;
con
   double z;
   z = 0;
   c = (a+sin(z))/(b+sin(z));
includendo la math.h e compilando con -lm e vedrai che dice
che sono diversi anche se ottimizzi.
Ale
-- 
Alessandro Russo


 
Come ho scritto prima, a/b nell'if *non* e' un double, perche'
e' calcolato e memorizzato internamente in precisione extra.
Infatti risulta diverso da c che era stato posto prima uguale ad a/b.
Pensavo che (double)(a/b) lo trasformasse comunque in double.
Ciao
Ale
-- 
Alessandro Russo


 precisamente l'operatore di casting?
probabilmente dipende dal compilatore, per esempio compilando con gcc 
-O2 ottengo equal sia nel primo sia nel secondo caso. col compilatore ms 
ottengo equal in entrambi i casi pure.
ciao.

 precisamente l'operatore di casting?
Dice al compilatore "Anche se a te sembra una cazzata quello che
sto facendo, fallo ugualmente e non segnalarmi avvisi/errori di
nessun tipo".
C:\>iao Angelo
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 che sono diversi anche se ottimizzi.
ho provato e con gcc ottengo in effetti ancora not equal (equal col 
compilatore ms a 0 ottimizzazioni).
a questo punto ti conviene adottare unastrategia tipo:
if (abs(c-(double)(a/b))<DBL_EPSILON)...
tenendo conto che:
/* minimum positive floating-point number x such that 1.0 + x != 1.0 */
#define FLT_EPSILON     1.192092896e-7f
#define DBL_EPSILON     2.2204460492503131e-16
#define LDBL_EPSILON    DBL_EPSILON
ciao.

         ....
    if(DBL_EPSILON > abs(c - a/b))
      ...
funziona meglio :-)


 
Ciao
Ale
-- 
Alessandro Russo
russo at matapp dot unimib dot it

 
Io sto solo cercando di capire come vanno le cose, non
devo risolvere nessun problema. Mi sembra che questo
comportamento metta in evidenza la non portabilita'
del codice.
Ciao


 il codice?
Direi di si.


 
Mi dai una referenza per favore?


 
Mi dai una refenza per favore?


 Mi dai una referenza per favore?
Google is your friend...


 approssimato a 64 bit.
a questo punto, dopo che hai ignorato tutte le ragionevoli spiegazioni, 
  mi sento in dovere di consigliarti di spendere i succitati 2000euro.
-- 
sbi

 
Il valore e' 1/3 approssimato a 80 bit, che e' diverso da 1/3
approssimato a 64 bit.
Ciao
Ale

> approssimato a 64 bit.
riassumo:
1) in C due float(double) con lo stesso valore possono avere
    RAPPRESENTAZIONI diverse.
2) in C la semantica dell'operatore == per float(double) confronta le
    RAPPRESENTAZIONI.
3) da 1) e 2): in C il confronto tra due RAPPRESENTAZIONI dello STESSO
    numero POSSONO "restituire" il valore logico false.
4) in C il RISULTATO degli operatori + - * / tra due float(double) è di
    tipo float(double).
c'è qualcosa che ancora non è chiaro o che non ti convince?
-- 
sbi

 apparentemente non fa niente.
Tieni conto che il compilatore, a seconda delle varie opzioni,
(e a seconda del compilatore!!) dietro le quinte puo' fare
un po' come gli pare, purche' certe cose siano garantite.
Nel caso di cui sopra, supponiamo anche che il compilatore
calcoli (a/b) in long double.
Pero' il fatto e' che dal suo punto di vista (a/b)
anche se internamente in quel momento e' rappresentato
a 80 bit, e' pur sempre un double.
Quindi quando va a fare il confronto con c non perde tempo
a "troncare" la rappresentazione di a/b, visto che
magari ce l'ha gia' bella e caricata in un qualche
registro.
Per questo motivo ti puo' dare una differnza diversa da
zero.
Tu gli diresti: ma come ! ti avevo detto di forzare
con un cast il risultato ad un double !
Il compilatore esce tutto sudato e ti risponde:
a fra', tu volevi un double, io ci ho messo altri 16
bit di precisione a gratisse, ma sono fatti miei,
tu una precisione fino a quella del double l'hai avuta.
Le conclusioni che vanno tratte sono 2
1) non si discute con i compilatori. Pur nel rispetto degli standard
fanno poi un po' quel che gli pare in quanto ad ordine
delle operazioni, ottimizzazioni, registri, etc.
2) e' meglio prendere l'abitudine di non usare test di uguaglianza tra 
numeri in floating point. A meno di casi particolari, tipo
un test per vedere se il valore e' rimasto a 0. perche'
non ci ho fatto sopra nulla.
Come ti hanno detto e' molto meglio stabilire una soglia Eps,
(che dipendera' non solo dalla precisione di macchina, ma
anche dai calcoli con i quali ottieni i numeri da confrontare) e invece 
di scrivere
if (a==b)
scrivi
if (fabs(a-b)<Eps)
ciao,
g.
-- 
AIUTARE= Accrescere il numero degli ingrati
(Ambrose Bierce, Il Dizionario del Diavolo)

 memorizzato su 80 bit invece che su 64.
La ragione e' che la macchina approssima. Dove si genera questa
approssimazione NON interessa ai fini della scrittura di programmi
C corretti, quello che interessa e' che l'uguaglianza dei valori
floating point si verifica come ti e' stato detto.
C:\>iao Angelo
P.S. sizeof(a/b) == sizeof(double) sia su gcc che su VC++.


 
Fai questo esempio:
main () {
double a,b;
long double la,lb,lc,x;
a = 1.0;
b = 3.0;
la = (long double) a;
lb = (long double) b;
lc = la/lc;
x = lc - (long double)(a/b);
printf("se a/b e' un long double, viene zero\n");
}
Vedrai che viene zero, perche' a/b FINCHE NON LO STORI
e' un long double.
Ciao
Ale

 Come ti hanno detto e' molto meglio (bla bla)
Lo so che cosa e' meglio, come ho gia' detto varie volte.
Di mestiere faccio il matematico all'universita' e mi
occupo di analisi numerica, per cui ho ben presente
queste cose. Siccome sto insegnando un corso di Calcolo
Numerico elementare, volevo chiarire alcune cose sull'
aritmentica fp.
Ciao
Ale

 
Ma a me (per motivi didattici) interessa proprio sapere
cosa fa la macchina in queste circostanze. Poi magari non
serve a niente, ma io vorrei saperlo.
Ale

 serve a niente, ma io vorrei saperlo.
La MACCHINA si comporta in maniera diversa a secondo del compilatore
che usi e a secondo delle opzioni che metti (non e' detto, ma e'
possibile).
gcc ha un long double di 12 byte (ergo 96 bit), VC++ ha un long double
di 8 byte. Gia' questo esclude qualsiasi generalizzazione tu vogli fare
sull'aritmetica dei floating point.
Quello che dovresti avere in mente e' che le specifiche del C
(soprattutto in certi punti) sono molto lasche.
_Probabilmente_ nel caso specifico (come ti ho gia detto) gcc genera
del codice in cui viene effettuato un confronto tra un registro del
coprocessore matematico e una locazione di memoria ed il risultato e'
che le due rappresentazioni sono diverse.
Ma questo ha un valore didattico molto relativo. Con un altro
compilatore (leggasi VC++) o con altre opzioni di compilazione
questo potrebbe NON essere piu' vero anche sulla stessa macchina
(e con lo stesso compilatore).
C:\>iao Angelo



Beh a me sembra invece molto importante didatticamente, proprio
perche' insegna a diffidare di queste standardizzazioni. Comunque
se tu pensi che non lo sia non importa.
Ciao
Ale
ps: comunque il long double del gcc (su x86) e' di 10 byte, non 12.
E mi sembra molto strano che il long double del VC++ sia uguale al
double. Comunque la mia domanda era un'altra.

     RAPPRESENTAZIONI.
Uhm... I problemi derivanti dall'uso dell'aritmetica in virgola mobile 
sono tanti ma i confronti non vengono fatti "tra rappresentazioni" ed i
numeri sono memorizzati (tranne che durante l'esecuzione dei calcoli
-calcoli, non confronti-) in formati (tipicamente IEEE 754) che
*impongono* un'unica rappresentazione per un numero.
Ciao.
-- 
BSD is for UNIX lovers -- linux is for Windows haters.

 ps: comunque il long double del gcc (su x86) e' di 10 byte, non 12.
No guarda. è di 12 (gcc version 3.3.4 su x86)
M.


L'allocazione (sizeof) e' 12, ma la precisione e' 10 (80 bit,
che e' la lunghezza dei registri del processore matematico).
Si puo' vedere cosi':
guarda il file float.h (sulla mia macchina
/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include/float.h)
dove sono definite tutte le quantita' relative all'aritmetica
floating point.
    /* Number of base-FLT_RADIX digits in the significand of a long 
double */
#undef LDBL_MANT_DIG
#define LDBL_MANT_DIG 64
cioe' 64 bit per la mantissa
    /* Minimum int x such that FLT_RADIX**(x-1) is a normalised long 
double */
#undef LDBL_MIN_EXP
#define LDBL_MIN_EXP (-16381)
esponente minimo = -16381
    /* Maximum int x such that FLT_RADIX**(x-1) is a representable long 
double */
#undef LDBL_MAX_EXP
#define LDBL_MAX_EXP 16384
esponente massimo = 16384
cioe' 15 bit per l'esponente (2^15 = 32768)
64 + 15 + 1 (segno) = 80
Si puo' comunque anche vedere direttamente calcolando la roundoff unit
eccetera.
Ciao
Ale
ps: un po' piu' sotto c'e' scritto:
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
    /* The floating-point expression evaluation method.
         -1  indeterminate
          0  evaluate all operations and constants just to the range and
             precision of the type
          1  evaluate operations and constants of type float and double
             to the range and precision of the double type, evaluate
             long double operations and constants to the range and
             precision of the long double type
          2  evaluate all operations and constants to the range and
             precision of the long double type
    */
# undef FLT_EVAL_METHOD
# define FLT_EVAL_METHOD	2
che e' esattamente quello che dicevo io a proposito della precisione
delle subespressioni.


Ho trovato questo articolo che mi pare faccia bene il punto
della situazione.
http://www.x86-64.org/lists/discuss/msg00023.html
A.

 Ciao,
[Dopo mille discussioni...]
Tuttavia intervengo a favore di Alessandro Russo su una cosa. Diciamo
che.. (tra mille virgolette), "ha ragione lui quando dice che l' (a/b)
all'interno della condizione if non è da considerarsi un double (durante
la comparazione)".
Ebbene sì, questa sera mi sono dilettato col gcc&gdb, quindi posso
asserire che: l'operazione di confronto è lasciata completamente in mano
al coprocessore, o meglio: supposto che le variabili a,b,c siano a
questi indirizzi sullo stack:
a = 0xfffffff8(%ebp
b = 0xfffffff0(%ebp)
c = 0xffffffe8(%ebp)
dopo aver inizializzato a = 1 e b = 3, queste sono le operazioni che gcc
fa fare al coprocessore:
fldl   0xfffffff8(%ebp) ; carica a nel registro %st0 del nostro 8087
fdivl  0xfffffff0(%ebp) ; dividi %st0 con b. Risultato in %st7
fstpl  0xffffffe8(%ebp) ; poppa ciò che è in %st0 il risultato in c
fldl   0xfffffff8(%ebp) ; pusha di nuovo a in %st0
fdivl  0xfffffff0(%ebp) ; dividici b (risultato in %st0)
fldl   0xffffffe8(%ebp) ; pusha c in %st0 (ma prima %st0 diventa %st1)
fucompp                 ; compara %st0 con %st1, imposta lo status
fnstsw %ax              ; leggi lo status e copialo in %ax
Quello che ora si trova in %ax è qualcosa che ci dice che la comparazione
non è andata a buon fine, infatti quando noi pushamo c (prima di
comparare), il risultato che è già presente all'interno del coprocessore
matematico ha una rappresentazione piu estesa del valore che andiamo ad
inserire (come voler comparare 0,333300 e 0,333333)
Ok,ok.. queste cose voi le sapevate già, però le ho riportate "in
difesa" di quello che diceva Alessandro: l'(a/b) non è proprio da
considerarsi un double (la sua rappresentazione infatti in %st1 è di 10
bytes contro gli 8 bytes di c (che per altro non viene paddato con degli
0, ma con strani valori che non so chi se li è inventati e perché))
Che scemotto gcc! sarebbe stato piu sicuro tirare fuori il risultato e
compararlo da te invece che far fare tutto a quel farabutto del
coprocessore.. si sa che lui con le sue extra precisioni fa scazzare tutto..
Byee
Matx

 double. Comunque la mia domanda era un'altra.
Invece e' proprio cosi', ho controllato. E non viola lo standard
(long double puo' anche essere uguale a double).
Ale


Caro Matx,
ti ringrazio molto per la tua analisi. Quello che mi ha lasciato
veramente sconcertato e' che anche "forzare" a/b a double con
(double)(a/b) non funziona, cioe' viene completamente ignorato.
Bisogna proprio costringere in qualche modo a/b in 8 byte.
Buona serata
Ale

 (double)(a/b) non funziona, cioe' viene completamente ignorato.
Questo perché, come ti è già stato spiegato, per il compilatore a/b è un
double anche se è calcolato dall'hw sottostante in precisione estesa
(per aumentare la precisione dei calcoli, anche se in questo caso 
*sembra* diminuirla).
Però, te lo ribadisco anche io, fare i confronti "bit a bit" tra i
numeri in virgola mobile per testarne l'uguaglianza *è* sbagliato a
prescindere dal fatto che a/b sia calcolato in precisione estesa o meno;
anche se esistessero solo ed esclusivamente i numeri a precisione
singola (float) e venissero calcolati esclusivamente a precisione
singola, sarebbe comunque sbagliato questo tipo di confronto (ed il
confronto corretto ti è già stato mostrato).
Se non capisci perché te lo posso spiegare in dettagglio questa sera
(ora devo "scappare").


 confronto corretto ti è già stato mostrato).
HO CAPITO. Come ho gia' spiegato varie volte, sono un docente
universitario di matematica (mi occupo di analisi numerica) e
sto facendo a lezione una introduzione all'aritmetica floating
point. Stavo cercando di capire bene come lo standard IEEE 754
dell'aritmetica floating point sia violato in una architettura
che presenta una precisione estesa (come x87). Guarda l'articolo
http://cch.loria.fr/documentation/IEEE754/ACM/addendum.html
Per fare un altro esempio, se si seguono le regole
dello standard IEEE 754 in doppia precisione (double) risulta
(0.3-0.2)-0.1 diverso da 0.3-(0.2+0.1)
Se invece faccio fare questo conto al gcc, risultano uguali
perche' le sottoespressioni sono calcolate in precisione estesa.
Per essere espliciti:
double a,b,c,x,y;
a=0.3;b=0.2;c=0.1;
x=(a-b)-c;
y=a-(b+c);
nello standard IEEE 754 ritornano
x=-2^(-55) = -2.77555756 × 10e-17
http://www.google.com/search?q=(0.3-0.2)-0.1
e y=-2^(-54) = -5.55111512 × 10e-17
http://www.google.com/search?q=0.3-(0.2%2B0.1)
(se vuoi ti faccio vedere i conti che ho fatto a mano
basandomi sullo standard IEEE 754).
Invece per il gcc sono uguali e valgono -2^(-55).
Io pensavo che scrivere
y = a - (double)(b+c);
avrebbe risolto il problema, invece non e' cosi'. Evidentemente
il casting in questo caso non viene fatto. Allora mi chiedo cosa
fa davvero il casting.
Ciao
Ale
ps: invece
double a,b,c,x,y;
a=0.3;b=0.2;c=0.1;
x=(a-b)-c;
y=(b+c);
y=a-y;
riporta in risultato conforme allo standard.

 Bisogna proprio costringere in qualche modo a/b in 8 byte.
...
Il tuo problema mi ha molto interessato. Ho seguito solo ieri sera tardi 
il thread e, a parte qualche intervento indebitamente spocchioso di chi 
non aveva capito il problema che ponevi, ci ho imparato molto.
Ho fatto un po' di controlli per conto mio.
Direi che tutto torna con il fatto che gcc (su x86) usa comunque la 
rappresentazione che e' nel registro. Percio' anche se l'espressione a/b 
e' double (se a e b lo sono), in realta' viene utilizzata l'espressione 
a precisione maggiore del registro.
Capito questo, resta il tuo problema originale: perche' il cast non 
effettua la conversione ?
Io sospetterei in un bug dell'implementazione del gcc.
Il motivo e'il seguente: come gia' ricordato, lo standard (99) prevede 
diversi formati di valutazione delle espressioni, governati dal valore 
di FLT_EVAL_METHOD. Il default de mio gcc(3.3.3)/linux/x86 e'  2 che 
corrisponde a valuatre operazioni e costanti nel range e alla precisione 
del tipo long double.
Pero'  in una nota alla sezione 6.5.4 (Cast operators) si legge:
"86) If the value of the expression is represented with greater 
precision or range than required by the type named by the cast, then the 
cast specifies a conversion even if the type of the expression is the 
same as the named type."
Questo, a me sembra voler dire che *secondo lo standard*, nel tuo 
esempio dovrebbe valere c == (double) (a/b)  anche se non fosse vero che 
c==a/b.
C'e'poi un'altra cosa che mi lascia perplesso nel comportamento di 
gcc/x86 ed e' che gcc ha un opzione -ffloat-store che (secondo man) 
dovrebbe agire nel sendo di
"Do not store floating point variables in registers, and inhibit
other options that might change whether a floating point value is
taken from a register or memory.
This option prevents undesirable excess precision on machines such
as the 68000 where the floating registers (of the 68881) keep more
precision than a "double" is supposed to have.  Similarly for the
x86 architecture.  For most programs, the excess precision does
only good, but a few programs rely on the precise definition of
IEEE floating point.  Use -ffloat-store for such programs, after
modifying them to store all pertinent intermediate computations
into variables."
A me non fa nessuna differenza!
Ciao
Giorgio

 fa davvero il casting.
Caro ex-collega, il punto e' che in questo caso il
cast non fa e non deve fare proprio nulla.
Da un punto di vista del linguaggio C l'espressione (b+c) e' gia'
double quindi e' molto verosimile che il cast
venga semplicemente scartato, allo stesso
modo che un compilatore con un ottimizzatore
farebbe decadere a=b+c+0.0;  in a=b+c; perche'
riconoscerebbe che la somma di 0.0 e' ridondante.
Non ti far fuorviare dal fatto che a te, "antropologicamente",
il comportamento del compilatore ti sembra poco corretto.
Esso e' tenuto, in base agli standard, a rispondere
correttamente entro una certa soglia di errore.
Quello che succede oltre la soglia e' in qualche
modo indefinito. Quindi non ci si puo' far conto.


 Pero'  in una nota alla sezione 6.5.4 (Cast operators) si legge:
...
Si tratta di una sezione dello standard 1999 !
Giorgio

 cast non fa e non deve fare proprio nulla.
...
Come ho riportato nell'altro post, sembrerebbe che invece lo standard 
richieda che il cast venga comunque eseguito!
Giorgio

 
Grazie, ciao
Ale

 ... Comunque la mia domanda era un'altra.
E la risposta ti e' stata data. Il cast serve per forzare la mano al
compilatore in modo che questo produca codice "corretto" anche in
situazioni in cui avrebbe da ridire.
double a = 9.0;
double b = 7.0;
int c = a%b;
Senza cast non verrebbe compilato. Con il cast viene generato codice
che esegue l'operazione (sinceramente non so se con risultati _sempre_
corretti). Nello specifico dell'x86 i double vengono convertiti in int
e ficcati nei registri giusti in modo che possa essere fatta una idiv.
Nell'espressione
y = a - (double)(b+c);
il cast e' inutile, in quanto tutti gli attori sono gia' double e il
compilatore NON ha bisogno di forzature (e sicuramente NON serve per
aumentare/diminuire la precisione dei calcoli).


 rispettare?
No, mi dispiace. Stavo solo cercando di rifrasare quello che
altri piu' agguerriti sugli standard mi sembrava ti avessero
gia' detto.
Io per fortuna faccio uso piu' che altro di numeri interi....
E molto tempo fa mi sono scontrato (2 giorni di debugging prima di
capire quale era il problema...)
esattamente con questo problema, anche se messo in modo meno esplicito.
ciao,
g.

 il cast e' inutile, in quanto tutti gli attori sono gia' double
Ma neghi l'evidenza! NON E' VERO che gli attori sono gia' in double,
perche' b/c (non b+c come ha scritto tu) non lo e'!
Guarda i post di Matic e di Giorgio Pastore su questo thread
che confermano quanto dico con molta piu' autorevolezza.
Ciao
Ale

 gia' detto.
La realta' e' il compilatore non deve rispettare nessuno standard
riguardo alla precisione dell'aritmetica. Questo e' il compito
del processore (o dell'emulatore sofware) che fa le operazioni.
Per LUI si' che esiste uno standard, che e' il 754 dell'IEEE
(che peraltro e' anche sotto revisione).
http://grouper.ieee.org/groups/754/
Purtroppo, come messo in evidenza in questo thread, la presenza
di uno standard a basso livello per l'aritmetica non assicura
affatto la portabilita' che e' (sarebbe!) molto importante per
chi si occupa di algortimi numerici, e "banali" correzioni
come la mia (il casting) non funzionano.
Ciao
Ale

 perche' b/c (non b+c come ha scritto tu) non lo e'!
b/c e' un double, ma nei calcoli interni _puo'_ essere trattato come
se fosse un long double: questo provoca problemi usando gcc.
Come ti e' stato detto, questo dipende da una impostazione di default
del gcc, e (forse) da una non perfetta aderenza allo standard di gcc.
E' proprio per il fatto che i floating point non possono essere
rappresentati con precisione infinita, e a causa delle diverse
strategie che i vari compilatori usano per affrontare questo problema,
che e' scorretto usare == per il confronto tra due double.
Se un compilatore "C" genera codice che fallisce il test
c = a/b;
if(DBL_EPSILON > abs(c - a/b))
e' il compilatore che sbaglia. Se un compilatore "C" genera codice che
fallisce il test
if(c == a/b)
non ha commesso nussun errore...
Ma non credo che riusciro' a convincerti di questo.


 
Ma se il codice generato da if(c == (double)(a/b)) fallisce il test,
il compilatore sbaglia o no? A me sembra di si'.
E' scorretto pensare che due double ottenuti da due operazioni
algebricamente identiche ma algoritmicamente diverse siano uguali,
e qui ci vuole il test che dici tu. Ma qui stiamo confrontando
due double ottenuti con la STESSA operazione, quindi la situazione
e' diversa.
Ciao, Ale

 
L'ho provato anch'io, e anche a me non fa nessuna differenza.
Pero' se leggi attentamente la man trovi (ultimo paragrafo):
 > Use -ffloat-store for such programs, after
 > modifying them to store all pertinent intermediate computations
 > into variables."
C'e' scritto "after modifying them to store all pertinent intermediate
computations into variables". Quindi sembra che uno dovrebbe:
- storare tutti i calcoli intermedi in variabili;
- usare -ffloat-store
A me sembra che se uno stora tutti i risultati intermedi in variabili,
-ffloat-store non sia piu' necessario; a meno che non si riferisca
a possibili ottimizzazioni.
Per controllare l'aritmetica ci sono anche le opzioni:
-mfpmath=sse -msse2
che sui pentium nuovi (e forse anche sugli athlon) non dovrebbero
piu' usare gli 80 bit ma lo standard IEEE, se ho capito bene.
In effetti con queste opzioni i miei esempi funzionano.
Ciao a dopo
Ale

 
Per fare un esempio esplicito: supponiamo di avere un insieme
composto da 10 double diversi, tipo
{sin(1), sin(2), ...., sin(10)}
Ho questo problema: ho un double, e voglio sapere se e' uno dei
numeri di prima.
L'unico test che ha senso e' ==; se uso quello con l'EPS
potrei avere un risultato sbagliato, perche' i numeri potrebbero
essere vicinissimi. Il test che dici tu ha senso solo
quando ho due double che provengono da algoritmi diversi
ma algericamente identici come 0.3-0.2-0.1 e 0.3-(0.2+0.1).
E' chiaro allora che non mi aspetto che siano ==, ma differiscono
dell'ordine della roundoff unit.
Ciao
Ale

 non ha commesso nussun errore...
ma, da quello che ho riportato dallo standard, dovrebbe invece NON 
fallire il test
assert( c == (double) (a/b) )
Peraltro, tra le prove che ho fatto finora mi sembra che solo gcc per 
x86 fallisce.
Giorgio

 il compilatore sbaglia o no? A me sembra di si'.
Alla luce di quanto riportato da Pastore...


 numeri di prima.
Quanto affermato ha un senso utilizzando una matematica con precisione
infinita. Su una CPU reale il metodo proposto per la verifica del fatto
che si sta usando uno dei numeri usati in precedenza e' sbagliato.
E' sempre possibile che due numeri sufficientemente vicini diano lo
stesso valore di sin() e anche == risulterebbe un metodo di verifica
sbagliato.



Non hai capito l'esempio. sin() non c'entra niente.
Lo riformulo. Supponiamo che io abbia per motivi miei
due insiemi finiti (o array, se preferisci) di double:
A={a_1,a_2,...,a_10}
e
B={b_1,b_2,....,b_10}
Adesso ho un double x che so che sta in uno dei due insiemi.
Mi spieghi come posso fare per sapere se x sta in A o in B?
Ale
-- 
Alessandro Russo
russo at matapp dot unimib dot it

 x86 fallisce.
Perche' gcc _internamente_ fa i calcoli con la precisione di un
long double. VC++ no. Non ho macchine diverse da x86 per fare altre
prove.


 
Ciao


 A={a_1,a_2,...,a_10}
Ok, ho capito male. Questo e' l'unico caso in cui puo' aver
senso usare ==.


 long double. 
Veramente non mi pare che li faccia solo _internamente_....


 Veramente non mi pare che li faccia solo _internamente_....
_SOLO_ internamente. Esternamente tutto e' double (con il C non si
ha accesso diretto ai registri utilizzati per i calcoli). Cio' non
toglie che l'effetto di qualcosa di interno non abbia riflessi anche
all'esterno: un confronto tra due registri ha come effetto il
fallimento o meno del test.


 
Veramente il mio esempio non mi sembra un "caso particolarissimo".
Comunque e' una questione di gusti. Cerco di riassumere il mio
pensiero:
- tutte le volte che due double sono il risultato di calcoli
   di algoritmi diversi, come dici tu non ha senso fare il confronto bit
   per bit;
- pero' se io calcolo "sin(34.3)" due volte mi pare di poter
   pretendere che siano uguali fino all'ultimo bit. O non sei d'accordo?


 Alla luce di quanto riportato da Pastore... cosa?
Il risultato dato da gcc di if(c ==(double)a/b) e' sbagliato ...


    pretendere che siano uguali fino all'ultimo bit. O non sei d'accordo?
a = sin(34.3);
b = sin(34.3);
a == b Si
a == sin(34.3) NO


 
e
a == (double)(sin(34.3))


 
Quindi pensi anche tu che sia un bug del gcc?


 diverse.
uguali sia con gcc che con ms.
ciao.

 Quindi pensi anche tu che sia un bug del gcc?
Prima di dire che e' un bug (e potrebbe esserlo) bisognerebbe
verificare lo standard a cui e' aderente la versione di ggc che
hai usato e cosa dice quello standard a proposito.
Lo stato attuale di conformita' di gcc 3.4 al C99 e' riportato qui.
http://gcc.gnu.org/gcc-3.4/c99status.html


 uguali sia con gcc che con ms.
ho provato per curiositą con altri compilatori: openwatcom dice che sono 
diversi, mentre il compilatore della intel dice che sono uguali, ma 
avvisa argutamente con uno warning:
warning #1572: floating-point equality and inequality comparisons are 
unreliable
ciao.


Questo significa che sia gcc sia ms usano i registri
interni per storare le subespressioni.
Prova questa variante che dovrebbe dare i risultati
diversi (e quindi "veri" rispetto allo standard IEEE 754):
double a,b,c,x,y;
a=0.3;
b=0.2;
c=0.1;
x = a-b-c;
y = b+c;
y = a-y;
E' divertente notare che se poi ottimizzi, ritornano uguali!


 unreliable
Bellissimo!
Ciao


 E' divertente notare che se poi ottimizzi, ritornano uguali!
x==y
gcc: false
ms: false
ow: true
icl: false
ciao.
p.s. gcc (3.4.2 mingw) da lo stesso risultato anche con le 
ottimizzazioni (-O2).

 
Questo mi sembra al di la' delle mie capacita'.
Ciao


 ottimizzazioni (-O2).
Hai ragione, anche ottimizzando restano diversi. Mi sono confuso
con l'altro esempio.
Se provi invece con i long double hai sempre x~=y sia scrivendo
y=a-(b+c);
oppure
y=b+c;
y=a-y;
Questo perche' non c'e' piu' la precisione extra.
Ciao


 Questo mi sembra al di la' delle mie capacita'.
http://gcc.gnu.org/lists.html
gcc-...@gcc.gnu.org
gcc-...@gcc.gnu.org
Qui dovrebbero essere in grado di darti una mano...


 Questo perche' non c'e' piu' la precisione extra.
x=y sempre con gcc.
ciao.


Questo non mi torna. Ti riporto il mio codice:
main() {
   long double a,b,c,x,y;
   a=0.3L;
   b=0.2L;
   c=0.1L;
   x = a-b-c;
   y = a-(b+c);
   printf("0.3-0.2-0.1= %Le\n",x);
   printf("0.3-(0.2+0.1)= %Le\n",y);
}
L'output e' (senza ottimizzazioni):
0.3-0.2-0.1= 6.776264e-21
0.3-(0.2+0.1)= 0.000000e+00
Cioe' sono diversi. Idem con
y = b+c;
y = a-y;
al posto di
y = a-(b+c);
Puoi provare per favore?


 Cioe' sono diversi. Idem con...
io li vedo perfettamente uguali (con e senza ottimizzazioni)! che le 
differenze siano dovute al porting mingw (la 3.4.2 che sto usando è una 
candidate)? :/
ciao.

 candidate)? :/
Non saprei.
Quanto viene il tuo risultato?


 Quanto viene il tuo risultato?
0 spaccato:  0.000000e+000
ciao.


Non capisco. Io ho fatto i conti a mano rispettando lo standard
dell'aritmetica e i risultati che vengono sono i miei che ti
ho riportato prima. Prova a testare questo coi float fatto in modo
da non usare la precisione extra:
main() {
   float a,b,c,x,y;
   a=0.3;
   b=0.2;
   c=0.1;
   x = (a-b)-c;
   y = b+c;
   y = a-y;
   printf("0.3-0.2-0.1= %e\n",x);
   printf("0.3-(0.2+0.1)= %e\n",y);
}
A me viene
0.3-0.2-0.1= 7.450581e-09
0.3-(0.2+0.1)= 0.000000e+00
Ale
# gcc -v
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man 
--infodir=/usr/share/info --enable-shared --enable-threads=posix 
--disable-checking --with-system-zlib --enable-__cxa_atexit 
--host=i386-redhat-linux
Thread model: posix
gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)


Direi che se si da'  una lettura a questa  pagina:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=14708
la questione e'    definitivamente risolta come un bug di gcc.
In particolare e' utile riportare la parte di standard che viene citata:
 >
 >The latest public draft of the C99 standard (WG14/N869) says:.
 >
 >In 
5.1.2.3
:
 >
 >       [#13]  EXAMPLE 4 Implementations  employing  wide  registers
 >       have to take care to honor  appropriate  semantics.   Values
 >       are  independent  of  whether  they  are  represented  in  a
 >       register or in memory.  For example, an implicit spilling of
 >       a  register  is  not permitted to alter the value.  Also, an
 >       explicit  store  and  load  is  required  to  round  to  the
 >       precision  of  the  storage  type.  In particular, casts and
 >       assignments  are  required  to   perform   their   specified
 >       conversion.  For the fragment
 >               double d1, d2;
 >               float f;
 >               d1 = f = expression;
 >               d2 = (float) expressions;
 >       the  values  assigned to d1 and d2 are required to have been
 >       converted to float.
In aggiunta, il contenuto della pagina risolve anche le perplessita' 
sul flag  -ffloat-store.
Giorgio

 gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
se ti puo' servire:
gcc -v
Reading specs from c:/programmi/mingw/bin/../lib/gcc/mingw32/3.4.2/specs
Configured with: ../gcc/configure --with-gcc --with-gnu-ld --with-gnu-as 
--host=mingw32 --target=mingw32 --prefix=/mingw --enable-threads 
--disable-nls --enable-languages=c,c++,f77,ada,objc,java 
--disable-win32-registry --disable-shared --enable-sjlj-exceptions 
--enable-libgcj --disable-java-awt --without-x --enable-java-gc=boehm 
--disable-libgcj-debug --enable-interpreter 
--enable-hash-synchronization --enable-libstdcxx-debug
Thread model: win32
gcc version 3.4.2 (mingw-special)
ciao.

 
Questo risultato e' corretto e perfettamente conforme
allo standard dell'aritmetica. Ho guardato sulle mlist
di mingw e mi pare che ci sia qualche problema con il
"long double", ma non ho capito bene. Adesso devo andare,
continuero' piu' tardi.
Ciao e grazie
Ale