Newsgroup: it.comp.java
Titolo: moltiplicazioni tra double... sbagliate!

Ciao a tutti. :)
ditemi che non sono pazzo... come è possibile questo?
dopo una domenica pomeriggio a controllare del codice che produce
risultati errati mi ritrovo a fronteggiare dei problemi di arrotondamento
nelle *moltiplicazioni*...
riassumendo in breve, anche il più semplice dei programmi non genera il
risultato che dovrebbe.
il mio codice esegue 2 conteggi su dati estratti da sql e deve verificare
se il totale dei due conteggi è uguale (un saldo dare e un saldo avere,
insomma)
facendo il debug riga per riga, saltano fuori strane cifre.
allora scrivo del codice da zero tanto per fare una prova, metto due
numeri a caso, il codice deve fare la moltiplicazione di 3.0 * 30.1, che
anche a mente sono in grado di risolvere a 90.3 eppure java mi calcola un
valore diverso, molto prossimo ma diverso.
ma come è possibile?
devo mettermi a controllare tutti i risultati anche delle moltiplicazioni?
questo è il codice che ho eseguito per il test.
public class Prova {
   public static void main(String[] args) {
      double a = 3.0;
      double b = 30.1;
      double c = a * b;
      double d = 90.3;
 
      System.out.println(c);
 
      if( c == d )
         System.out.println("tutto ok");
      else
         System.out.println("santo cielo... :(");
   }
}
e l'output è:
90.30000000000001
santo cielo... :(
stesso risultato con J2SE 5.0 update 5 su Linux e su Windows e anche con
J2SE 6.0 rc61 su Linux, in tutto su tre macchine diverse.
a me non sembra per niente normale, eppure non sto facendo nulla di
speciale... capita solo a me?
ciao e grazie
gl

ciao ancora :)
mi rispondo da solo citando quello che ho trovato su google.
http://groups.google.it/group/comp.lang.java.programmer/browse_frm/
thread/2c8ac3c0614197d/60282dbeca39b64c
quindi è così che funziona.
non so come mai me ne sono accorto solo ora, ma sta di fatto che non si
possono confrontare con certezza due operandi di un'operazione e il loro
risultato.
ciao
gl :)

 nelle *moltiplicazioni*...
Tutto sotto controllo! ;)
E' ben noto che le operazioni tra double risentono di questi
"problemi" di arrotondamento, intrinsechi alla notazione numerica
in virgola mobile su elaboratore (e quindi *non* e' un problema solo di Java).
Per ottenere risultati corretti *devi* usare le classi
java.math.BigInteger e java.math.BigDecimal.
Ciao
Roberto

Breve ma ottimo articolo:
http://www.nvitalia.com/articoli/Floating_Point/4.htm
E un po' di (s)considerazioni personali:
http://www.megadix.org/calcoli_impossibili.html
-- 
  -- + Setaceous Pig + --
http://www.megadix.org/
http://www.jroller.com/page/megadix/

 santo cielo... :(
http://docs.sun.com/source/806-3568/ncg_goldberg.html
Saluti
CarMas

 writes:
RB> Per ottenere risultati corretti *devi* usare le classi
RB> java.math.BigInteger e java.math.BigDecimal.
Oppure  smazzarsi la  teoria del  calcolo numerico  e le  tecniche per
tenere l'errore sotto controllo :)
-- 
 /\            ___
/___/\__|_|\_|__|___Gian Uberto Lauri_____________________
  //--\ | | \|  |   Integralista GNUslamico  
\/		    e allevatore di bug da competizione

 tenere l'errore sotto controllo :)
E' quello che hanno fatto i simpatici amici di java.math.*, no? ;-)
Certo che il numero di domande sui floating point che danno risultati 
"errati" è preoccupante... una volta era una delle prime cose che ti 
insegnavano.
bye
DKano

 writes:
D> Certo che il numero di domande sui floating point che danno
D> risultati "errati" è preoccupante... una volta era una delle prime
D> cose che ti insegnavano.
Esatto, e prima di farti toccare una tastiera vera :).


 Esatto, e prima di farti toccare una tastiera vera :).
beh, che vi devo dire...
nei tempi che furono sapevo anche eseguire le divisioni in binario (con
l'ausilio di carta e penna)
sta di fatto che da un linguaggio di alto livello, mi sembra lecito
aspettarsi un minimo di sostegno. evidentemente non è così.
grazie a tutti :)
ciao
gl :)

 writes:
gi> beh, che vi devo dire...  nei tempi che furono sapevo anche
gi> eseguire le divisioni in binario (con l'ausilio di carta e penna)
gi> sta di fatto che da un linguaggio di alto livello, mi sembra
gi> lecito aspettarsi un minimo di sostegno. evidentemente non è così.
Non può essere così.
Le operazioni  sugli interi  sono una cosa,  quelle in  floating point
un'altra.  Ma  proprio un'altra.  Cominciamo col fatto  che i computer
lavorano  in  una  base  che  è  una  potenza  di  due,  per  cui  già
rappresentare  un  valore  banale  com  0.1  in  decimale  da  qualche
problemino:
Decimale: 0.1
Binario : 0.00011001100110011001100110011001100110011
che  come  vedi  è  periodico,  quindi non  può  essere  rappresentato
correttamente.
Poi   ci  sono   i  problemi   di   perdita  di   precisione  con   le
moltiplicazioni,  il  fatto  che  i  numeri floating  point  non  sono
distribuiti  uniformemente  (tra  1e-28  e  2e-20 non  c'è  la  stessa
distanza che c'è tra 1e28 e 2e28).
A  Ingegneria,  e  presumo  anche  a  Fisica  e  nelle  altre  facoltà
scientifiche  dove  fanno risolvere  per  problemi  per via  numerica,
venivano dibattute in  un corso apposta che peraltro  era il primo che
si affrontava sulle cose relative alla programmazione.
Quando si fanno programmi scientifici si considerano valide solo poche
cifre  decimali e  si  procede ad  arrotondamenti applicando  tecniche
apposite  per contenere  l'errore  entro limiti  previsti (quelli  che
appunto ti fanno considerare buone solo alcune delle cifre decimali).
Dicevo, con gli interi è una  cosa, con i decimali un'altra. Quando si
fanno  calcoli   finanziari  non  si   usano  i  floating   point,  ma
rapprezentazioni con un numero  finito di cifre decimali, praticamente
deli interi "con la virgola spostata".


Il problema e' che credi che i double siano numeri reali. In questo ti 
sbagli ed il problema sta' li'.

 un'altra.  
Aggiungiamoci anche un altro esempietto:
        double a=2.0;
        double b=Double.MAX_VALUE;
        double m=(a*b)/a;
        System.out.println(m);
Prova a farlo girare e te ne accorgi :D
Saluti
CarMas

 writes:
C> double a=2.0; double b=Double.MAX_VALUE; double m=(a*b)/a;
C> System.out.println(m);
Questa  č crudelitudinevolezza! :),  anche gli  interi, una  volta che
sono  andati  in  overflow  non  sanno  piů da  dove  sono  partiti  e
moltiplicanto  Double.MAX_VALUE  per  2  ottieni  Inf  perché  vai  in
overflow.


 sono  andati  in  overflow  non  sanno  più da  dove  sono  partiti  
Vero, ma si era partiti dai double e io ho creato l'esempio sui double :P
Saluti
CarMas

 e l'output č:


 writes:
L> Anche a me dava lo stesso errore, poi ho usato i float e ho
L> risolto... ma non so perché.
Magari  studiandosi  la  teoria  che  c'è sotto  il  calcolo  numerico
(personalmente la trovo pallosetta) si capisce che diavolo succede.
Non è roba banale, per nulla.
