Errore di precisione con float in Java

Mi chiedo quale sia il modo migliore per correggere gli errori di precisione in Java. Come puoi vedere nel seguente esempio, ci sono errori di precisione:

class FloatTest { public static void main(String[] args) { Float number1 = 1.89f; for(int i = 11; i < 800; i*=2) { System.out.println("loop value: " + i); System.out.println(i*number1); System.out.println(""); } } } 

Il risultato visualizzato è:

valore del ciclo: 11

20.789999

valore del ciclo: 22

41.579998

valore del ciclo: 44

83.159996

valore del loop: 88

166,31,999 mila

valore del loop: 176

332,63,998 mila

valore del loop: 352

665,27,997 mila

valore del loop: 704

1330.5599

Inoltre, se qualcuno può spiegare perché lo fa solo a partire da 11 e raddoppiando il valore ogni volta. Penso che tutti gli altri valori (o molti di essi almeno) mostrino il risultato corretto.

Problemi come questo mi hanno causato mal di testa in passato e di solito uso i formattatori di numeri o li inserisco in una stringa.

Edit: Come ho detto, potrei usare un double, ma dopo averlo provato, sembra che 1.89 come una doppia volta 792 emetta ancora un errore (l’output è 1496.8799999999999).

Credo che proverò con le altre soluzioni come BigDecimal

Se ti interessa davvero la precisione, dovresti usare BigDecimal

http://download.oracle.com/javase/1,5.0/docs/api/java/math/BigDecimal.html

Il problema non è con Java ma con il buon standard float ( http://en.wikipedia.org/wiki/IEEE_floating-point_standard ).

Puoi:

  • usa Double e hai un po ‘più di precisione (ma non è perfetto, naturalmente, ha anche una precisione limitata)

  • usa una libreria di precisione arbitraria

  • utilizzare algoritmi numericamente stabili e cifre troncate / rotonde di cui non si è certi siano corretti (è ansible calcolare la precisione numerica delle operazioni)

Quando si stampa il risultato di una doppia operazione è necessario utilizzare l’arrotondamento appropriato.

 System.out.printf("%.2f%n", 1.89 * 792); 

stampe

 1496.88 

Se si desidera arrotondare il risultato a una precisione, è ansible utilizzare l’arrotondamento.

 double d = 1.89 * 792; d = Math.round(d * 100) / 100.0; System.out.println(d); 

stampe

 1496.88 

Tuttavia, se vedi qui sotto, viene stampato come previsto, in quanto vi è una piccola quantità di arrotondamenti impliciti.


Non vale niente che (double) 1.89 non sia esattamente 1.89 È un’approssimazione ravvicinata.

new BigDecimal (double) converte il valore esatto di double senza alcun arrotondamento implicito. Può essere utile per trovare il valore esatto di un doppio.

 System.out.println(new BigDecimal(1.89)); System.out.println(new BigDecimal(1496.88)); 

stampe

 1.8899999999999999023003738329862244427204132080078125 1496.8800000000001091393642127513885498046875 

Potresti usare i doppi invece dei galleggianti

Se hai davvero bisogno di precisione arbitraria, usa BigDecimal .

La maggior parte delle tue domande è stata trattata abbastanza bene, anche se potresti ancora trarre vantaggio dalla lettura del wiki del tag [floating-point] per capire perché funzionano le altre risposte.

Tuttavia, nessuno ha risposto “perché lo fa solo a partire da 11 e raddoppiando il valore ogni volta”, quindi ecco la risposta a questo:

 for(int i = 11; i < 800; i*=2) ╚═══╤════╝ ╚╤═╝ │ └───── "double the value every time" │ └───── "start at 11" 

prima di Float è la class wrapper per il float primitivo

e i doppi hanno più precisione

ma se si desidera calcolare solo fino alla seconda cifra (ad esempio per scopi monetari) utilizzare un numero intero (come se si utilizzino i centesimi come unità) e aggiungere una logica di ridimensionamento quando si sta moltiplicando / dividendo

o se hai bisogno di precisione arbitraria usa BigDecimal

Se la precisione è vitale, è necessario utilizzare BigDecimal per assicurarsi che rimanga la precisione richiesta. Quando installi il calcolo, ricorda di usare le stringhe per istanziare i valori invece dei doppi.