2016-10-16 2 views
-2

Dies kann eine sehr dumme Frage erscheinen, aber ich begegnete etwas Geheimnisvolles und scheinbar schön heute. Ich habe versucht herum zu googlen und konnte nichts graben.Genaue Darstellung unrepresentable Gleitkommazahlen in Java

Ich bin mir bewusst, dass 0,1 nicht im Binärformat dargestellt werden kann. Und doch, wenn ich den folgenden Code ausführen:

double g = 1.0; 
    System.out.println(g); 
    g = g/10; 
    System.out.println(g); 
    g = g*3; 
    System.out.println(g); 

Dies erzeugt die Ausgabe:

1.0 
0.1 
0.30000000000000004 

Die erste Ausgabe und die dritte Ausgabe erwartet, aber was mit dem zweiten ist da los? Warum ist es, nun, richtig? Das sollte unmöglich sein, und doch, da ist es.

+2

Können Sie bitte die Ausgänge sind Sie gesehen haben, so dass wir, um Ihren Code nicht zu kompilieren und wieder laufen? – Nayuki

+2

Der Ausgang ist er immer ist wahrscheinlich: '1.0',' 0.1', '0.30000000000000004' – Addison

+1

Anzeige genügend Ziffern rechts vom Komma, und Sie werden den Unterschied sehen. Bitte lesen Sie [Was jeder Informatiker über Fließkomma-Arithmetik wissen sollte] (https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). –

Antwort

1

Warum ist es gut, richtig?

Wie Sie bemerkt, viele Dezimal-Gleitkommazahlen nicht als binäre Gleitkommazahlen und umgekehrt dargestellt werden.

Wenn Sie schreiben eine Anweisung wie folgt:

double g = 0.1; 

der Dezimalwert auf den nächst binär Gleitkommawert umgewandelt wird. Und wenn Sie dann drucken Sie es mag dieses

System.out.println(g); 

die Formatierer die nächste Dezimalstelle erzeugt Gleitkommawert nach folgenden Regeln:

Wie viele Stellen müssen für den Bruchteil gedruckt werden? Es muss mindestens eine Ziffer geben, um den Bruchteil darzustellen, und darüber hinaus so viele, aber nur so viele, mehr Ziffern, wie erforderlich sind, um den Argumentwert von angrenzenden Werten vom Typ double eindeutig zu unterscheiden.

(Referenz: Double.toString(double) javadoc)

Das bedeutet, dass Sie oft die genaue Dezimaldarstellung der Dezimalzahl, die Sie mit gestartet.

In einfachen Worten, der Fehler von dezimal binär bei der Umwandlung ist das gleiche wie die Fehler, wenn sie von binärer Umwandlung in Dezimalzahlen. Die Fehler "löschen" aus.

Nun ist dies nicht immer der Fall. Oft sind die kumulativen Fehler in der Berechnung groß genug, die Fehler in den Dezimal (und binären) Ergebnissen werden in der Ausgabe offensichtlich sein.

+0

Es stellt sich heraus die Javadoc ist falsch, und [Java kann die kürzeste Zeichenfolge, die Umläufe nicht immer produzieren] (http://www.exploringbinary.com/java-doesnt-print -die-kürzeste-Strings-die-Runde-Trip /) (das hat jedoch keinen Einfluss auf deine Antwort). –

1

Numerische Promotion-Regeln

  1. Wenn zwei Werte verschiedene Datentypen haben, Java wird automatisch eine der Förderung der Wer- ues auf die größere der beiden Datentypen.
  2. Wenn einer der Werte verbunden ist und das andere ist Gleitkommazahlen, Java wird automatisch fördern den Integralwert auf den Datentyp des-Fließkommawert.
  3. Kleinere Datentypen, nämlich byte, short und char, werden zuerst in int eingeordnet sie werden mit einem binären arithmetischen Java-Operator verwendet, auch wenn keiner der Operanden int ist.
  4. Schließlich Förderung aufgetreten ist, und die Operanden haben den gleichen Datentyp, der result- ing Wert den gleichen Datentyp wie seine gefördert Operanden haben.
+0

Ja, aber es gibt mehr zu der Geschichte – Nayuki

0

uns durch die Berechnungszeilen Schritt Let:

double g = 1.0; 

g die float64 Zahl, die genau 1,0.

g = g/10; 

Der rechte Operand wird in double konvertiert, also ist es genau 10.0.

Der Divisionsvorgang wird bei unendlicher Genauigkeit (konzeptuell) durchgeführt wird, und dann aufgerundet auf die nächste float64 Nummer als das Ergebnis.

Die genaue Antwort ist deutlich 0,1. Aber die nächste float64-Zahl zu 0.1 ist genau 7205759403792794/256.

Daher g = 0.10000000000000000555111512312578 ... (mehr Ziffern). Wenn Sie den genauen genauen Wert drucken möchten, sehen Sie sich den neuen BigDecimal (g) an.

g = g * 3; 

Auch hier wird der rechte Operand genau in 3.0 konvertiert. Wir multiplizieren 0.1000000000000000055511151231257 (...) mit 3, um 0.3000000000000000166533453693773 (...) zu erhalten. Der Wert von g ist nun genau 5404319552844596/2 .