Konvertieren Schwimmer doppelt ohne Präzision zu verlieren
-
06-09-2019 - |
Frage
Ich habe einen primitiven Schwimmer und ich brauche als primitive Doppel. zu verdoppeln, gibt mir seltsam zusätzliche Präzision einfach den Schwimmer Gießen. Zum Beispiel:
float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375
Wenn jedoch anstelle von Gießen, I-Ausgang der Schwimmer als String und die Zeichenfolge als Doppel analysieren, ich bekommen, was ich will:
System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35
Gibt es einen besseren Weg, als zu String zu gehen und zurück?
Lösung
Es ist nicht, dass Sie wirklich zusätzliche Präzision bekommen - es ist, dass der Schwimmer nicht genau die Zahl darstellten Sie ursprünglich anstrebten. Die doppelte ist , die den ursprünglichen Schwimmer genau; toString
wird die „extra“ Daten, die zeigen, die bereits vorhanden war.
Zum Beispiel (und diese Zahlen sind nicht richtig, ich mache die Dinge nur nach oben): Angenommen, Sie haben:
float f = 0.1F;
double d = f;
Dann könnte der Wert von f
genau ,100000234523. d
haben genau den gleichen Wert, aber wenn man es in einen String konvertieren es „Vertrauen“, dass es auf eine höhere Präzision genau ist, so wird nicht so früh abzurunden, und Sie werden die „zusätzlichen Ziffern“ sehen, die waren schon da, aber von Ihnen versteckt.
Wenn Sie in eine Zeichenfolge und zurück konvertieren, werden Sie mit einem doppelten Wert zu enden, die näher an dem String-Wert ist als der ursprüngliche Schwimmer war - aber das ist nur gut , wenn Sie glauben wirklich, dass der String-Wert ist das, was Sie wirklich wollen.
Sind Sie sicher, dass Float / Double der entsprechenden Arten sind hier zu verwenden, statt BigDecimal
? Wenn Sie versuchen, Zahlen zu verwenden, die eine genaue Dezimalwerte haben (zum Beispiel Geld), dann BigDecimal
ist eine angemessenere Art IMO.
Andere Tipps
Ich finde, in der binären Darstellung Umwandlung leichter, dieses Problem zu erreichen.
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)));
Sie können sehen, der Schwimmer auf die doppelte erweitert wird von 0s bis zum Ende, aber, dass die doppelte Darstellung von 0,27 ist ‚genauer‘, damit das Problem hinzuzufügen.
111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000
Dies ist aufgrund der Vertrag von Float.toString(float)
, die teilweise sagt:
Wie viele Ziffern müssen gedruckt werden der Bruchteil [...]? Dort muss mindestens eine Ziffer sein, stellt den Bruchteil, und darüber hinaus, dass so viele, aber nur so viele, mehr Stellen als nötig sind, um eindeutig zu unterscheidet das Argument Wert von benachbarte Werte vom Typ float. Das wird angenommen, dass x die genaue ist mathematischer Wert, der durch die dargestellte Dezimaldarstellung hergestellt von diese Methode für ein endliches ungleich Null Argument f. Dann muss f sein der Schwimmer Wert am nächsten x; oder, wenn zwei Schwimmer Werte sind ebenso nahe an x, dann f muss einer von ihnen sein und die am wenigsten Bit der Mantisse von f muss 0 sein.
Ich habe dieses Problem heute begegnet und nicht refactor zu BigDecimal verwenden konnte, weil das Projekt wirklich riesig. Allerdings fand ich Lösung mit
Float result = new Float(5623.23)
Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()
Und das funktioniert.
Beachten Sie, dass Aufruf result.doubleValue () gibt 5623,22998046875
Aber Aufruf doubleResult.doubleValue () gibt korrekt 5.623,23
Aber ich bin nicht ganz sicher, ob sein eine richtige Lösung.
Verwenden Sie einen BigDecimal
statt float
/ double
. Es gibt eine Menge von Zahlen, die (beispielsweise 0.1
) nicht als binäre Gleitkomma dargestellt werden. So müssen Sie entweder immer um das Ergebnis zu einer bekannten Präzision oder BigDecimal
verwenden.
Siehe http://en.wikipedia.org/wiki/Floating_point für weitere Informationen .
Ich fand die folgende Lösung:
public static Double getFloatAsDouble(Float fValue) {
return Double.valueOf(fValue.toString());
}
Wenn Sie float und double statt Float und Double verwenden Sie die folgenden Schritte aus:
public static double getFloatAsDouble(float value) {
return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}
Floats, von der Natur, sind ungenau und haben immer ordentlich Rundung „Probleme“. Wenn Präzision wichtig ist, dann könnten Sie Ihre Anwendung Refactoring Dezimal oder BigDecimal zu verwenden.
Ja, den Schwimmer ist rechnerisch schneller als Dezimalzahlen, weil die auf dem Prozessor-Unterstützung. Allerdings müssen Sie schnell oder genau wollen?
Weitere Informationen dazu kommt unter Punkt 48 - Vermeiden float und double, wenn genaue Werte erforderlich sind, von Effective Java 2. Auflage von Joshua Bloch. Dieses Buch ist Marmelade mit guten Sachen gepackt und auf jeden Fall einen Blick wert.
funktionierts?
float flt = 145.664454;
Double dbl = 0.0;
dbl += flt;