2013-03-25 16 views
22

Così, quando aggiungo o sottraggo in Java con Doubles, mi danno strani risultati. Eccone alcuni:L'aggiunta e la sottrazione di doppi danno strani risultati

Se aggiungo 0.0 + 5.1, mi dà 5.1. È corretto.

Se aggiungo 5.1 + 0.1, mi dà 5.199999999999 (Il numero di ripetizioni 9 s potrebbe essere spento). È sbagliato.

Se sottraggo 4.8 - 0.4, mi dà 4.39999999999995 (Anche in questo caso, il ripetuto 9 s potrebbe essere spento). È sbagliato.

All'inizio pensavo che questo fosse solo il problema con l'aggiunta di doppi con valori decimali, ma mi sbagliavo. Di seguito ha funzionato bene:

5.1 + 0.2 = 5.3 
5.1 - 0.3 = 4.8 

Ora, il primo numero aggiunto è un doppio salvato come variabile, anche se la seconda variabile afferra il testo da un JTextField. Per esempio:

//doubleNum = 5.1 RIGHT HERE 
//The textfield has only a "0.1" in it. 
doubleNum += Double.parseDouble(textField.getText()); 
//doubleNum = 5.199999999999999 
+0

btw, preferisce utilizzare BigDecimal (http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigDecimal.html) – user2147970

+0

In realtà 5.19999999 ... (con infiniti ripetuti 9's) è matematicamente identico a 5.2, se non ci credi, prova a scoprire qual è la differenza tra i due. È 0, quindi sono uguali. – Ingo

+0

@Ingo tecnicamente, sì, ma mi piacerebbe avere una risposta un po 'precisa :) –

risposta

24

In Java, double valori sono IEEE floating point numbers. A meno che non siano un potere di 2 (o somme di poteri di 2, ad esempio 1/8 + 1/4 = 3/8), non possono essere rappresentati esattamente, anche se hanno un'alta precisione. Alcune operazioni in virgola mobile comporteranno l'errore di arrotondamento presente in questi numeri in virgola mobile. Nei casi che hai descritto sopra, gli errori in virgola mobile sono diventati abbastanza significativi da apparire nell'output.

Non importa quale sia la fonte del numero, sia che analizzi una stringa da JTextField o che specifichi un valore letterale double - il problema è ereditato dalla rappresentazione in virgola mobile.

Soluzioni alternative:

  • Se sai che dovrai solo hanno tanti punti decimali, quindi utilizzare intero aritmetica, poi convertire in un numero decimale:

    (double) (51 + 1)/10 
    (double) (48 - 4)/10 
    
  • Usa BigDecimal

  • Se è necessario utilizzare double, è possibile ridurre gli errori in virgola mobile con il Kahan Summation Algorithm.

+0

Pensi che se ho usato 'float's invece che dia un valore più preciso? –

+0

Un 'float' generalmente ha solo metà della precisione di un' double'. Non è una buona idea se hai bisogno della precisione. – rgettman

+0

@Ingo: buona presa; Ho aggiornato la mia risposta. – rgettman

2

In Java, doppie uso IEEE 754 numeri in virgola mobile (vedi this articolo di Wikipedia), che è intrinsecamente imprecisa. Utilizzare BigDecimal per una perfetta precisione decimale. Per arrotondare la stampa, accettando semplicemente la precisione "abbastanza buona", utilizzare printf("%.3f", x).

Problemi correlati