Convertire float a doppia senza perdere in precisione
-
06-09-2019 - |
Domanda
Ho un galleggiante primitivo e ho bisogno come un doppio primitivo. Basta lanciare il galleggiante di raddoppiare mi dà strano precisione in più. Ad esempio:
float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375
Tuttavia, se invece di casting, ho in uscita il galleggiante come una stringa, e analizzare la stringa come un doppio, ottengo ciò che voglio:
System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35
C'è un modo migliore di andare a String e ritorno?
Soluzione
Non è che sei effettivamente di ottenere una precisione ancora maggiore - è che il galleggiante non rappresentava con precisione il numero si stava puntando in origine. Il doppio è che rappresenta il galleggiante originale con precisione; toString
sta mostrando i dati "extra" che era già presente.
Per esempio (e questi numeri non sono giuste, sto solo facendo le cose) si supponga di avere:
float f = 0.1F;
double d = f;
Quindi il valore di f
potrebbe essere esattamente 0,100000234523. d
avrà esattamente lo stesso valore, ma quando si converte in una stringa sarà "fiducia" che è preciso per una maggiore precisione, in modo da non concludere quanto prima, e vedrete le "cifre in più", che erano già lì, ma nascosta da voi.
Quando si converte in una stringa e indietro, si sta finendo con un doppio valore che è più vicino al valore della stringa che il galleggiante originale era - ma questo è solo un bene se si crede davvero che il valore della stringa è quello che si voleva davvero.
Sei sicuro che float / double sono i tipi appropriati di utilizzare qui invece di BigDecimal
? Se si sta cercando di usare i numeri che hanno valori decimali precisi (ad esempio denaro), poi BigDecimal
è un tipo più appropriato IMO.
Altri suggerimenti
Trovo la conversione alla rappresentazione binaria più facile da afferrare questo problema.
float f = 0.27f;
double d2 = (double) f;
double d3 = 0.27d;
System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d2)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d3)));
Si può vedere il galleggiante è espanso al doppio aggiungendo 0s alla fine, ma che la doppia rappresentazione di 0,27 è 'più accurata', quindi il problema.
111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000
Ciò è dovuto al contratto di Float.toString(float)
, che dice in parte:
Quante cifre deve essere stampato per la parte frazionaria [...]? Là deve essere almeno una cifra rappresentano la parte frazionaria, e al di là che, come molti, ma solo come tanti, più cifre per quanto necessario a univocamente distinguere il valore dell'argomento da valori adiacenti di tipo float. Tale è, supponiamo che x è l'esatto valore matematico rappresentato dalla rappresentazione decimale prodotto da questo metodo per un diverso da zero finita argomento f. Allora f deve essere il galleggiante valore più vicino a x; o, se due galleggiante I valori sono ugualmente vicino a x, allora f deve essere uno di essi e l'almeno significativo bit della significando di f deve essere 0.
Ho incontrato questo problema oggi e non poteva usare refactoring per BigDecimal, perché il progetto è davvero enorme. Tuttavia ho trovato soluzione utilizzando
Float result = new Float(5623.23)
Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()
E questo funziona.
Si noti che chiama result.doubleValue () restituisce 5.623,22998046875
Ma chiamando doubleResult.doubleValue () restituisce correttamente 5.623,23
Ma non sono del tutto sicuro se è una soluzione corretta.
Utilizzare una BigDecimal
invece di float
/ double
. Ci sono molti numeri che non può essere rappresentato come punto binario galleggianti (per esempio, 0.1
). In modo che sia sempre necessario arrotondare il risultato con una precisione noto o utilizzare BigDecimal
.
http://en.wikipedia.org/wiki/Floating_point per ulteriori informazioni .
ho trovato la seguente soluzione:
public static Double getFloatAsDouble(Float fValue) {
return Double.valueOf(fValue.toString());
}
Se si utilizza float e doppia al posto di Float e Doppio utilizzare il seguente:
public static double getFloatAsDouble(float value) {
return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}
Galleggianti, per natura, sono imprecisi e hanno sempre "problemi" pulito arrotondamento. Se la precisione è importante allora si potrebbe prendere in considerazione il refactoring l'applicazione per utilizzare decimale o BigDecimal.
Sì, i galleggianti sono computazionalmente più veloce di decimali a causa del sul supporto del processore. Tuttavia, vuoi veloce o accurata?
Per informazioni questo viene alla Voce 48 - Evitare float e double quando sono necessari valori esatti, di Effective Java 2 ° edizione di Joshua Bloch. Questo libro è pieno di roba buona e sicuramente vale la pena dare un'occhiata.
Fa questo lavoro?
float flt = 145.664454;
Double dbl = 0.0;
dbl += flt;