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
- Perché Double.MIN_VALUE non è negativo
- I compilatori JIT di JVM generano codice che utilizza istruzioni in virgola mobile vettorizzate?
- bit mobili e aliasing rigoroso
- Quanto è deterministico l'inesattezza in virgola mobile?
- In che modo il computer esegue l'aritmetica in virgola mobile?
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.