Frage Konvertieren Sie float zu double ohne Genauigkeit zu verlieren


Ich habe ein primitives Float und ich brauche ein primitives Double. Einfach den Float auf Double zu übertragen gibt mir eine seltsame Extra-Präzision. Beispielsweise:

float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375

Wenn ich jedoch anstelle von Casting den Float als String ausgeben und den String als Double parsen lasse, bekomme ich, was ich will:

System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35

Gibt es einen besseren Weg als nach String und zurück?


76
2018-05-27 14:39


Ursprung


Antworten:


Du bist es nicht tatsächlich eine extra Genauigkeit zu erhalten - es ist, dass der Float nicht genau die Nummer repräsentiert hat, auf die Sie ursprünglich abzielten. Der Doppelgänger ist das original float genau darstellen; toString zeigt die "extra" -Daten, die bereits vorhanden waren.

Zum Beispiel (und diese Zahlen sind nicht richtig, ich mache nur Dinge) Unterstützung, die Sie hatten:

float f = 0.1F;
double d = f;

Dann der Wert von f könnte genau 0,100000234523 sein. d wird genau den gleichen Wert haben, aber wenn Sie es in eine Zeichenkette umwandeln, "vertraut" es, dass es auf eine höhere Genauigkeit genau ist, also wird es nicht so früh abrunden, und Sie werden die "zusätzlichen Ziffern" sehen, die bereits waren dort, aber vor dir verborgen.

Wenn Sie in eine Zeichenkette und zurück konvertieren, erhalten Sie einen doppelten Wert, der näher am Zeichenkettenwert liegt als die ursprüngliche Zeichenkette - aber das ist nur gut ob Sie glauben wirklich, dass der String-Wert das ist, was Sie wirklich wollten.

Sind Sie sicher, dass float / double die geeigneten Typen sind, die hier verwendet werden sollen? BigDecimal? Wenn Sie versuchen, Zahlen mit genauen Dezimalwerten (z. B. Geld) zu verwenden, dann BigDecimal ist ein geeigneterer Typ IMO.


102
2018-05-27 14:42



Ich finde die Umwandlung in die binäre Darstellung leichter, dieses Problem zu verstehen.

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, dass der Float auf das Doppelte erweitert wird, indem am Ende 0s hinzugefügt werden, aber dass die doppelte Darstellung von 0.27 "genauer" ist, daher das Problem.

   111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000

31
2018-02-07 08:45



Dies ist aufgrund des Vertrags von Float.toString(float), was teilweise sagt:

Wie viele Ziffern müssen gedruckt werden?   der Bruchteil [...]? Dort   muss mindestens eine Ziffer haben   den Bruchteil darstellen, und   darüber hinaus so viele, aber nur so viele, mehr Ziffern als benötigt werden, um eindeutig zu sein   Unterscheide den Argumentwert von   benachbarte Werte vom Typ float. Das   ist, nehme an, dass x genau ist   mathematischer Wert repräsentiert durch die   Dezimaldarstellung von   Diese Methode für ein endliches Nicht-Null   Argument f. Dann muss f der Schwimmer sein   Wert am nächsten zu x; oder, wenn zwei schweben   Werte sind gleich nahe bei x, dann f   muss einer von ihnen sein und der geringste   bedeutendes Bit des Signifikanten von   f muss 0 sein.


22
2018-05-27 14:56



Ich bin heute auf dieses Problem gestoßen und konnte keinen Refactor für BigDecimal verwenden, weil das Projekt wirklich riesig ist. Ich habe jedoch eine Lösung gefunden

Float result = new Float(5623.23)
Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()

Und das funktioniert.

Beachten Sie, dass das Aufrufen von result.doubleValue () 5623.22998046875 zurückgibt

Aber das Aufrufen von doubleResult.doubleValue () gibt korrekt 5623.23 zurück

Aber ich bin mir nicht ganz sicher, ob es eine richtige Lösung ist.


14
2018-01-02 12:24



Benutze einen BigDecimalAnstatt von float/double. Es gibt viele Zahlen, die nicht als binäre Gleitkommazahl dargestellt werden können (z. B. 0.1). Sie müssen das Ergebnis also immer auf eine bekannte Präzision oder Verwendung runden BigDecimal.

Sehen http://en.wikipedia.org/wiki/Floating_point für mehr Informationen.


6
2018-05-27 14:45



Ich habe folgende Lösung gefunden:

public static Double getFloatAsDouble(Float fValue) {
    return Double.valueOf(fValue.toString());
}

Wenn du benutzt schweben und doppeltAnstatt von Schweben und Doppelt benutze folgendes:

public static double getFloatAsDouble(float value) {
    return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}

4
2018-01-25 16:20



Schwimmer sind von Natur aus unpräzise und haben immer ordentliche Rundungsfragen. Wenn die Genauigkeit wichtig ist, sollten Sie in Erwägung ziehen, Ihre Anwendung auf Decimal oder BigDecimal umzustellen.

Ja, Floats sind aufgrund der On-Prozessor-Unterstützung rechnerisch schneller als Dezimalstellen. Wollen Sie jedoch schnell oder genau?


1
2018-05-27 14:55



Zu Information kommt dies unter Punkt 48 - Vermeiden Sie Float und Double, wenn exakte Werte benötigt werden, von Effective Java 2nd edition von Joshua Bloch. Dieses Buch ist voller guter Sachen und definitiv einen Blick wert.


0
2018-05-27 15:05



Funktioniert das?

float flt = 145.664454;

Double dbl = 0.0;
dbl += flt;

0
2018-03-04 12:18