2015-06-11 10 views
8

Ich benutze jdk 1.8.0_45, und unsere Tests entdeckt einen Fehler in rouding. RoundingMode.HALF_DOWN funktioniert genauso wie RoundingMode.HALF_UP, wenn die letzte Nachkommastelle, die die Rundung entscheiden, 5.RoundingMode.HALF_DOWN Problem in Java8

ich ähnliche Probleme mit RoundingMode.HALF_UP gefunden, aber sie fixiert sind in Update 40. Ich habe auch einen Fehler der Orakel, aber aus meiner Erfahrung sind sie wirklich unempfänglich.

package testjava8; 

import java.math.RoundingMode; 
import java.text.DecimalFormat; 

public class Formatori { 

    public static void main(String[] args) { 
     DecimalFormat format = new DecimalFormat("#,##0.0000"); 
     format.setRoundingMode(RoundingMode.HALF_DOWN); 
     Double toFormat = 10.55555; 
     System.out.println("Round down"); 
     System.out.println(format.format(toFormat)); 

     format.setRoundingMode(RoundingMode.HALF_UP); 
     toFormat = 10.55555; 
     System.out.println("Round up"); 
     System.out.println(format.format(toFormat)); 
    } 
} 

Tatsächliches Ergebnis: Abrunden 10,5556 Aufrunden 10,5556

Erwartetes Ergebnis (erhalten mit jdk 1.7): Abrunden 10,5555 Aufrunden 10,5556

+2

Dieser Fehler reproduziert nicht mit jdk 1.8.0_45.Und ich habe das Thema gelesen, das du gesagt hast, aber es ist nicht das gleiche Problem. Dieses Problem wurde behoben. – cristi

Antwort

10

Es scheint, dass es beabsichtigte Änderung. Das Verhalten von JDK 1.7 war nicht korrekt.

Das Problem ist, dass man einfach nicht die Nummer 10.55555 mit dem double Typ darstellen. Es speichert die Daten im IEEE-Binärformat. Wenn Sie also die dezimale 10.55555-Nummer der double-Variablen zuweisen, erhalten Sie tatsächlich den nächsten Wert, der im IEEE-Format dargestellt werden kann: 10.555550000000000210320649784989655017852783203125. Diese Zahl ist größer als 10.55555, daher ist sie korrekt auf 10.5556 in HALF_DOWN Modus gerundet.

Sie können einige Zahlen überprüfen, die im Binärformat genau dargestellt werden können. Zum Beispiel 10.15625 (das ist 10 + 5/32, also 1010.00101 binär). Diese Zahl wird auf 10.1562 in HALF_DOWN Modus und 10.1563 in HALF_UP Modus gerundet.

Wenn Sie das alte Verhalten wiederherstellen möchten, können Sie zunächst Ihre Nummer BigDecimal mit BigDecimal.valueOf Konstruktor konvertieren, die „ein double in ein BigDecimal übersetzt, die double‚s kanonische String-Darstellung unter Verwendung“:

BigDecimal toFormat = BigDecimal.valueOf(10.55555); 
System.out.println("Round down"); 
System.out.println(format.format(toFormat)); // 10.5555 

format.setRoundingMode(RoundingMode.HALF_UP); 
toFormat = BigDecimal.valueOf(10.55555); 
System.out.println("Round up"); 
System.out.println(format.format(toFormat)); // 10.5556 
+0

Beachten Sie, dass Java Unterstriche in Zahlenliteralen erlaubt. Die Verwendung von ihnen, um den Punkt für die Rundung zu markieren, kann den Lesern durch den Haufen von '5' helfen ... – Holger

+1

Ich war im Begriff, eine ähnliche Antwort zu posten, da das Verhalten mir richtig erschien. Dies wäre das Problem [JDK-7131459] (https://bugs.openjdk.java.net/browse/JDK-7131459). Wie auch immer es scheint, sollte es in [7u40] (https://bugs.openjdk.java.net/browse/JDK-8000978) zurückversetzt worden sein, also warum tritt es mit 7u80 auf? Oder war das vielleicht ein anderer Bug? –

+1

Die DecimalFormat-Klasse sagt nicht, ob es mit der exakten oder der kanonischen Darstellung des Double funktionieren wird (es sei denn, ich habe es verpasst), also scheinen beide Verhaltensweisen akzeptabel zu sein ... – assylias

5

die Änderung des Verhaltens in the release notes of Java 8

dokumentiert ist, wenn die NumberFormat und DecimalFormat Klassen, die ro Das Verhalten früherer Versionen des JDK war in einigen Fällen falsch. [...]

Als Beispiel bei der Verwendung des Standard empfohlen NumberFormatFormat API Form: NumberFormat nf = java.text.NumberFormat.getInstance() gefolgt von nf.format(0.8055d), wird der Wert 0.8055d im Computer als 0,80549999999999999378275106209912337362766265869140625 aufgezeichnet, da dieser Wert nicht genau im binären Format dargestellt werden kann. Hier ist die Standard-Rundungsregel "halb-gerade" und das Ergebnis des Aufrufs von format() in JDK 7 ist eine falsche Ausgabe von "0.806", während das korrekte Ergebnis "0.805" ist, da der von der Computer ist "unter" der Krawatte.

Dieses neue Verhalten wird auch für alle Rundungspositionen implementiert, die durch ein vom Programmierer ausgewähltes Muster definiert werden können (keine Standardmuster).