2017-08-03 2 views
0

Ich schreibe ein Programm, das rohe doppelte Werte aus einer Datenbank nimmt und sie in 8-Byte Hex-Strings konvertiert, aber ich weiß nicht, wie man Genauigkeitsverlust verhindern kann. Die von allen Geräten empfangenen Daten werden doppelt gespeichert, einschließlich der 8-Byte-Identifikationswerte.Wie vermeide ich Genauigkeitsverlust mit Double in Java?

Instanzen von Doppelpunkten wie 7.2340172821234e + 16 werden korrekt ohne Genauigkeitsverlust analysiert, wobei der Exponent 10^16 ist.

In Fällen jedoch, in denen der Exponent 10^17 ist, verliert Java die Genauigkeit. Zum Beispiel 2.88512954935019e + 17 wird von Java als 1.44464854248327008E17

interpretiert Der Code, den ich wie folgt aussieht bin mit:

public Foo(double bar) { 
    this.barString = Long.toHexString((long) bar); 
    if (barString.length == 15) { 
     barString = "0" + barString; //to account for leading zeroes lost on data entry 
    } 
} 

ich dies einen Testfall ähnlich wie verwende es zu testen:

@Test 
public void testFooConstructor() { 
    OtherClass other = new OtherClass(); 

    OtherClass.Foo test0 = other.new Foo(72340172821234000d); //7.2340172821234e+16 
    assertEquals("0101010100000150", test0.barString); //This test passes 

    OtherClass.Foo test1 = other.new Foo(144464854248327000d);//1.44464854248327e+17 
    assertEquals("02013e0500000758, test1.barString); //This test fails 
} 

Die Unit-Test lautet:

Expected: 02013e0500000758 
Actual: 02013e0500000760 

Wenn ich p rucken aus den Werten, die Java gespeichert 72340172821234000d und 144464854248327000d wie es jeweils druckt:

7.2340172821234E16

1.44464854248327008E17

Dieser Wert ist um 8 ab, die für die konsistent zu sein scheint Wenige, die ich getestet habe.

Kann ich irgendetwas tun, um diesen Fehler zu korrigieren?

EDIT: Dies ist kein Problem, wo mich interessiert, was ist vorbei an der Stelle. Die Frage, von der einige denken, dass es sich um ein Duplikat handelt, lautet, warum Fließkommazahlen weniger präzise sind. Ich frage, wie man den Verlust von Genauigkeit vermeiden kann, und zwar durch ähnliche Umgehungslösungen wie die, die Roman Puchkovskiy vorgeschlagen haben.

+1

Sie können nicht. 'double' hat eine begrenzte Genauigkeit. –

+0

Versuchen Sie mit BigDecimal – DwB

+0

@DwB Oder, wenn es eine ganze Zahl ist, BigInteger. – Dukeling

Antwort

2

Sie könnten Ihre Gleitkommawerte aus der Datenbank übernehmen als Strings (und Punkte nicht schwimmend) und BigDecimal dann wandeln sie in long verwenden:

String fpAsString = getFromDB(); 
long longValue = new BigDecimal(fpAsString).longValue(); 
this.barString = Long.toHexString(longValue); 

BigDecimal .longValue() ist analog zu verengen primitive Umwandlung von double bis long, aber es verliert nicht die Genauigkeit (abgesehen vom Verlust des Bruchteils). Sie können etwas verlieren, wenn das Ergebnis nicht in long passt, aber das gleiche passiert mit Ihrer Besetzung zu long.

0

Float- und Double-Typen sind Variablen, die sehr gut zum Speichern sehr großer Zahlen oder sehr kleiner Zahlen, aber sehr schlecht zum Speichern von Zahlen mit einer großen Anzahl von Ziffern geeignet sind, und zwar aufgrund ihrer binären Darstellung.

Wenn man sich einmal ansieht, wie Double oder Float im Speicher gespeichert sind, gibt es ein Bit für das Vorzeichen, mehrere Bits für den Exponenten und mehrere Bits für den Bruch.

Wenn also, wie der Wert der Suche in dem Speicher tatsächlich gespeichert ist, ist es so etwas wie diese:

bits

Und der tatsächliche Wert wird wie folgt berechnet:

formula

(Dieses Beispiel bezieht sich auf Float, das mit 32 Bits dargestellt ist, wobei Double mit 64 Bits dargestellt ist, aber die gleichen Prinzipien gelten)

Die Anzahl der Ziffern, die die Zahl darstellen kann, ist auf die Anzahl der Stellen beschränkt, die der Bruchteil darstellen kann, aber selbst bei einer sehr begrenzten Anzahl von Stellen können Doppel und Gleitkommazahlen sehr große Zahlen und sehr kleine Zahlen darstellen.

In Java Double, der Bruchteil Teil nehmen 52 Bits, wenn Sie in den Rechner einchecken, was ist die größte Zahl eine 52-Bit-Nummer sein kann (formula2) Sie werden sehen, dass Sie eine 16-stellige Nummer erhalten. Double kann größere Zahlen darstellen als Nullen vor oder nach der Verwendung der Zahl, die durch den Exponenten dargestellt wird. Es kann jedoch keine Zahl mit mehr als 16 Ziffern ohne Genauigkeitsverlust speichern.

Beachten Sie, es gibt tatsächlich mehr dazu und dies ist nur eine sehr einfache Erklärung für Double und Float-Darstellung. Wenn Sie zu der genauen Erklärung tauchen möchten, können Sie diese Wikipedia-Seite überprüfen: https://en.wikipedia.org/wiki/Single-precision_floating-point_format

Verwandte Themen