(Se non siete interessati in teoria, scorrere fino alla fine, c'è la correzione per il codice)
La ragione è molto semplice: Come sapete, il sistema binario supporta solo 0
s e 1
s
Quindi, diamo un'occhiata a vostri valori, e ciò che essi sono in rappresentazione binaria:
0.1 - 0.0001100110011001100110011001100110011001100110011001101
0.2 - 0.001100110011001100110011001100110011001100110011001101
0.3 - 0.010011001100110011001100110011001100110011001100110011
0.4 - 0.01100110011001100110011001100110011001100110011001101
0.5 - 0.1
0.6 - 0.10011001100110011001100110011001100110011001100110011
0.7 - 0.1011001100110011001100110011001100110011001100110011
0.8 - 0.1100110011001100110011001100110011001100110011001101
0.9 - 0.11100110011001100110011001100110011001100110011001101
che cosa vuol dire? 0.1
è un decimo di 1.Nessun problema nel sistema decimale, basta spostare il separatore di una posizione. Ma in binario il numero di telefono non può essere espresso 0,1 - perché ogni turno del segno decimale equivale a *2
o /2
- a seconda della direzione. (E 10 non può essere diviso in X turni di 2)
Per valori che si desidera dividere per multipli di 2 - si ottiene un risultato esatto:
1/2 - 0.1
1/4 - 0.01
1/8 - 0.001
1/16- 0.0001
and so on.
Pertanto cercando di calcolare un /10
è un infinita lungo risultato, che viene troncato quando il valore esaurisce i bit.
Detto questo, si tratta di una limitazione del modo in cui i computer funzionano, che tale valore non può mai essere memorizzato con precisione completa.
Site Nota: Questo "fatto" è stata ignorata la Patriot sistema inducendolo a diventare inutilizzabile dopo alcune ore di tempo di funzionamento, vedere qui: http://sydney.edu.au/engineering/it/~alum/patriot_bug.html
Ma perché funziona per ogni cosa tranne 0.7 + 0.1 - si potrebbe chiedere
Se si verifica il codice con 0.8
- funziona - ma non con 0.7 + 0.1
.
Ancora, in binario entrambi i valori sono già imprecisi. Se si somma entrambi i valori, il risultato è ancora più impreciso, portando ad un risultato sbagliato:
Se si somma 0,7 e 0,1 (dopo il separatore decimale) si ottiene questo:
0.101100110011001100110011001100110011001100110011001 1000
+ 0.000110011001100110011001100110011001100110011001100 1101
---------------------------------------------------------
0.110011001100110011001100110011001100110011001100110 0101
Ma 0.8 sarebbe
0.110011001100110011001100110011001100110011001100110 1000
Confronta gli ultimi 4 bit e la nota, che il conseguente "0.8" della somma è minore se si desidera convertire 0.8
in binario direttamente.
Indovinate un po ':
System.out.println(0.7 + 0.1 == 0.8); //returns false
Quando si lavora con i numeri si dovrebbe impostare voi stessi un limite di precisione - e numeri SEMPRE rotonde di conseguenza per evitare tali errori (non troncare!):
//compare doubles with 3 decimals
System.out.println((lim(0.7, 3) + lim(0.1, 3)) == lim(0.8, 3)); //true
public static long lim(double d, int t){
return Math.round(d*10*t);
}
Per avere il codice corretto: arrotondarlo a 4 cifre, prima di troncare dopo la prima cifra:
public static double truncate(double x){
long y = (long)((Math.round(x*10000)/10000.0)*10);
double z = (double)y/10;
return z;
}
System.out.println(truncate(0.7+0.1)); //0.8
System.out.println(truncate(0.8)); //0.8
Questo troncerà ancora come desiderato ma garantisce che un sarà arrotondato a 0.7
prima di troncarlo. È possibile impostare la precisione richiesta per l'applicazione. 10, 20, 30, 40 cifre?
Altri valori rimarranno corretti, perché qualcosa come 0.58999
sarà arrotondato a 0.59 - così ancora troncati come 0.5
e non arrotondato a 0.6
L'ho provato, Se il tuo input è 0,7 + 0,1, allora sarà calcolato male a 0,79999999999999999 – maskacovnik
Ma questo non accade con tutti gli altri doppi - 0.1, 0.2 ..etc – user1612078
I valori doppi sono imprecisi. Dovresti invece pensare a usare BigDecimal. – Tom