Newsgroup: it.comp.lang.c++
Titolo: Scusate, ma io proprio non capisco: differenza fra float

Ragazzi scusate, mesi fa avevo gia' posto questo quesito, poi ho lasciato
perdere ricorrendo ad altre soluzioni. Ora mi e' capitato lo stesso errore,
e, capisco che i numeri float vengono memorizzati in maniera particolare, ma
non e' possibile che un processore dal costo di svariate centinaia di
migliaia di lire non sia in grado di dare un risultato corretto quando la
mia calcolatrice avuta in omaggio col fustino di Dash ci riesce
benissimo!!!!
Praticamente, in Builder 3, ho 3 caselle di testo Edit1, Edit2 e EditRes:
Edit1->Text = 95,45
Edit2->Text = 80
questo e' il codice:
float mc1, mc2, res;
mc1 = (float)Edit1->Text.ToDouble();
mc2 = (float)Edit2->Text.ToDouble();
res = mc1 - mc2;
EditRes->Text = res;
RISULTATO: 15,4499969482422
MA COME %&$&£& SI FA?
Capisco tutti i discorsi sull'arrotondamento, ma qui si esagera!!!
Scusate :-(
--
Byez
Stefano Tempesta
temp...@tin.it
http://tempesta.freeweb.org

Scusate :-(
Potrei agevolmente accettare l'asserzione che tu sia esagerando
nel continuare a lamentarti del funzionamento dell'aritmetica sui
computer invece di fare un minimo di sforzo per capirci qualcosa
(ed eventualmente, se hai necessita` di soluzioni particolari, per
metterle in piedi).  Scusare questa ostinazione e` piu` arduo.
Alex

Caro Alex, apprezzo il fatto che hai dedicato del tempo a leggere il mio
messaggio e ancora di piu' che hai dedicato del tempo per rispondere. Ho
molto rispetto per le persone competenti che possono e vogliono mettere a
disposizione degli altri le proprie conoscenze ed esperienze (cosa che tu
fai puntualmente e, chi frequenta il ng, ti si riconosce indubbiamente).
Pero' permettimi di farti notare che molto spesso l'aria "arrogante di
saputello" con cui dai le risposte potrebbe essere mal vista, soprattuto da
chi chiede delucidazioni su argomenti ***apparentemente*** incomprensibili,
come quello che per un computer i numeri 15,4499969 e 15,4500031 sono
uguali: questo non e' accettabile (per me!).
Se, come tu dici, i float hanno una precisione di 6 cifre significative,
perche' mai, ottengo 15,4499969482422, un numero con ben piu' di 8 cifre? Ci
si poteva limitare a 15,449996, non trovi? Cos'e' tutta quella spazzatura in
coda al numero?
Quello che chiedevo, poi, era solo una soluzione al problema, non la
spiegazione scientifico-filosofica del problema (che apprezzo ma che, de
coccio come sono, non ho ben afferrato, a quanto pare).
Sulla mailing list C++Warriors ho posto lo stesso problema e gentilmente mi
hanno risposto che dovevo usare i numeri BCD che sono in base decimale: il
Builder offre proprio questa classe adatta allo scopo, ma se io non la
conosco, posso mai immaginarne l'esistenza? SCUSA L'IGNORANZA!!!
Spero che non fraintendi il tono di questa risposta, che e' molto placato:
non volevo rispondere per non alimentare discussioni ma mi sono sentito in
dovere di farlo per chiarire ogni cosa.
Scusate (nuovamente) per l'argomento! (Per la cronaca, ho usato i double al
posto dei float ed ottengo 15,45 come voluto, quindi tutto risolto!)
Ultima domanda: per evitare incazzature (mie) in futuro e per evitare di
postare altri messaggi simili (cosi' Alex non si adira (!) piu' :-))), mi
dite se esite una funzione che faccia il round di un numero come le funzioni
ARROTONDA.ECCESSO e ARROTONDA.DIFETTO di Excel?
Grazie a tutti per la cortese attenzione!


 uguali: questo non e' accettabile (per me!).
Questo argomento ``incomprensibile'', la rappresentazione dei numeri
in memoria, e' alla base di qualunque argomento di informatica.
Parlare di C++ senza sapere queste cose e' l'equivalente informatico
del voler discutere la Divina Commedia senza sapere quante (e quali)
sono le vocali.  Fa benissimo il buon Alex ad arrabbiarsi con chi
pretende di non studiare le cose con metodo ed avere comunque dei
risultati.
-- 
Maurizio Loreti                      
http://wwwcdf.pd.infn.it/~loreti/mlo.html
Un. of Padova, Dept. of Physics - Padova, Italy          
lor...@padova.infn.it

risultati.
Grazie della solidarieta`, ma guarda che non mi arrabbio: magari
mi "sfogo" un po', questo si`:-).
In specifico, il problema di Tempesta pare che riguardi solo e
specificamente i numeri in virgola mobile, quindi, chissa`, potrebbe
essere anche un grande mago di argomenti di informatica che alla
virgola mobile non afferiscono:-).
Personalmente, ho fatto in vita mia abbastanza svarioni (soprattutto
in _analisi numerica_, argomento che continuo a considerare uno
dei piu` ostici dell'informatica, pur avendolo approfondito e praticato
non poco!) che non posso non provare solidarieta` per chi, umano
quanto me, quanto me umanamente sbaglia -- il che non m'impedisce
di assestare (metaforiche e verbali) "bastonate" (come quelle che
ho preso io a suo tempo!-) a chi pare meritarle...P-)
Alex

ARROTONDA.ECCESSO e ARROTONDA.DIFETTO di Excel?
L'approccio generale per arrotondare un numero positivo a N cifre
decimali dopo la virgola, scegliendo ad esempio di arrotondare per
difetto, e`:
    double round(double d, unsigned int N) {
        double pow10 = pow(10, N);
        long result = static_cast<long>(d*pow10);
        return result/pow10;
    }
Volendo arrotondare per eccesso, basta cambiare
il return in
        return (result+pow10-1)/pow10;
e per arrotondare al piu` vicino, in
        return (result+pow10/2)/pow10;
Per evitare i vari problemi connessi all'uso di
queste semplici formule con numeri negativi,
conviene inoltre iniziare estraendo separatamente
segno e valore assoluto:
        double sign = 1;
        if(d<0) {
            sign = -1;
            d = -d;
        }
e naturalmente, nel return, moltiplicare quanto
si sta ritornando per sign.
In questo approccio, piu` che le due operazioni di
moltiplicazione e divisione, l'operazione piu` lenta
e` il calcolo di pow (alcuni compilatori potrebbero
ottimizzarla, ma non conviene farci affidamento).
Visto che, sicuramente, in ogni dato programma ci
interessa chiamare round solo con una piccola
varieta` di valori di N, questo problema si presta
molto bene alla semplice pattern di ottimizzazione
denominata "Cache", sottotitolo "Non ricalcolare,
memorizza!".
Usiamo dunque una funzione ausiliaria:
    double pow_10(unsigned int N) {
        static double pows[] = { 1,
            10, 100, 1000, 10000,
            100000, 1000000, 10000000,
            100000000, 1000000000 };
        if(N>9) return pow(10,N);
        return pows[N];
    }
e, nella round, cambiamo la 1a istruzione in:
        double pow10 = pow_10(N);
Qui usiamo valori precalcolati (a mano:-) per
N<=9, altrimenti eseguiamo i calcoli a runtime;
approccio pragmatico, pero` non molto generale.
Ecco, in alternativa, un'applicazione piu` fedele
della piu` generale pattern Cache:
    double pow_10(unsigned int N) {
        static std::vector<double> pows;
        if(N>=pows.size()) {
            int old_size = pows.size();
            pows.resize(N+1);
            if(old_size == 0) {
                pows[0] = 1;
                old_size = 1;
            }
            for(int i=old_size; i<=N; ++i)
                pows[i] = 10 * pows[i-1];
        }
        return pows[N];
    }
Questo e` l'approccio piu` efficiente.  Piu`
elegante, ma marginalmente meno efficiente,
sarebbe, nel corpo del test "if(N>=pows.size())":
        if(pows.empty()) pows.push_back(1);
        while(N>=pows.size())
            pows.push_back(10*pows.back());
Lo "special-case" pows.empty() si puo`
naturalmente evitare, a prezzo di una
piccola quantita` di memoria statica...:
    double pow_10(unsigned int N) {
        static double[] u = {1.0};
        static std::vector<double> 
            pows(u, u+1);
        while(N>=pows.size())
            pows.push_back(10*pows.back());
        return pows[N];
    }
E` forte la tentazione di adottare quest'ultimo
approccio, per la sua particolare eleganza,
con il "while" _invece_ dell'"if", l'uso dell'indirizzo
"off-the-end" u+1, ecc; l'eventuale difficolta` di
comprensione per un futuro manutentore spinge
pero` nell'altro verso.  Se non altro, volendo
adottare questo approccio, merita almeno
documentarlo copiosamente!-)
Alex

 ARROTONDA.ECCESSO e ARROTONDA.DIFETTO di Excel?
Non mi sembra che esista una funzione del genere ma puoi utilizzare
funzioni come ceil() per ottenere gli arrotondamenti che desideri (es.
ceil( numero * 1000 ) / 1000 arrotonda ai millesimi). Sicuramente ci
sono metodi più efficienti. Comunque credo che il tuo problema sia più
nella visualizzazione di tali numeri che nella loro rappresentazione
interna. Vedi quindi cose tipo il manipolatore setprecision() per
ottenere visualizzazioni con le cifre decimali che ritieni opportune.
EMG

 Purtroppo è così. Quello che forse ti confonde è forse la differenza tra
Uguali no (probabilmente differiscono per 1 bit: "ULP", "Unit of Least
Precision", e` la terminologia comunemente adattata dagli analisti
numerici), ma, se vogliono essere rappresentazioni del numero 15.45,
sono affetti dallo stesso errore relativo (e questo e` facile da vedere:
il _segno_ della differenza fra numero esatto e numero rappresentato,
infatti, non rileva nella definizione di "errore relativo") e quindi, in
particolare, hanno lo stesso numero di cifre significative (sette).
Alex

[..]
Ottimo, grazie per la risposta!
Non alimento tutto quello scritto in precedenza, su cui sono assolutamente
d'accordo: il (mio) problema e' che quando non riesco a trovare una
soluzione ad una cosa cosi' banale mi incazzo, e quando mi incazzo sparo un
po' di puttanate. Quindi niente di personale.
Il fatto di cercare informazioni nell'help del Builder e' un po' come
cercare il classico ago nel classico pagliaio: mai visto nessuna
documentazione fatta peggio (sic!)
