2015-06-11 16 views
8

Sto usando jdk 1.8.0_45, ei nostri test hanno scoperto un bug nel rouding. RoundingMode.HALF_DOWN funziona allo stesso modo RoundingMode.HALF_UP quando l'ultimo decimale che decidono l'arrotondamento è 5.Problema RoundingMode.HALF_DOWN in Java8

ho trovato questioni connesse con RoundingMode.HALF_UP, ma sono fissati in aggiornamento 40. Ho anche messo un bug al oracolo, ma dalla mia esperienza sono davvero insensibili.

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)); 
    } 
} 

effettivo risultato: rotonda giù 10,5556 rotonda fino 10,5556

Risultato atteso (ottenere con JDK 1.7): rotonda giù 10,5555 rotonda fino 10,5556

+2

Questo bug non riproduce con JDK 1.8.0_45.E ho letto il problema che hai detto, ma non è lo stesso problema. Questo problema è stato risolto. – cristi

risposta

10

Sembra che si tratta di cambiamento previsto Il comportamento di JDK 1.7 era errato.

Il problema è che semplicemente può non rappresentare il numero 10.55555 utilizzando il tipo double. Memorizza i dati nel formato binario IEEE, quindi quando si assegna il numero decimale 10.55555 alla variabile double, si ottiene effettivamente il valore più vicino che può essere rappresentato nel formato IEEE: 10.555550000000000210320649784989655017852783203125. Questo numero è più grande di 10.55555, quindi è arrotondato correttamente a 10.5556 nella modalità HALF_DOWN.

È possibile controllare alcuni numeri che possono essere esattamente rappresentati in binario. Ad esempio, 10.15625 (che è 10 + 5/32, quindi 1010.00101 in binario). Questo numero è arrotondato a 10.1562 nella modalità HALF_DOWN e 10.1563 nella modalità HALF_UP.

Se si desidera ripristinare il vecchio comportamento, si può prima convertire il tuo numero di BigDecimal utilizzando BigDecimal.valueOf costruttore, che "traduce un double in una BigDecimal, utilizzando rappresentazione di stringa canonica 's il double":

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

Si noti che Java consente il trattino basso nei numeri letterali. Usarli per segnare il punto per l'arrotondamento può aiutare i lettori attraverso il gruppo di '5's ... – Holger

+1

Stavo per pubblicare una risposta simile poiché il comportamento mi sembrava corretto. Questo sarebbe un problema [JDK-7131459] (https://bugs.openjdk.java.net/browse/JDK-7131459). Tuttavia sembra che avrebbe dovuto essere [backported in 7u40] (https://bugs.openjdk.java.net/browse/JDK-8000978) quindi perché si verifica con 7u80? O forse questo era un bug diverso? –

+1

La classe DecimalFormat non dice se funzionerà sulla rappresentazione esatta o canonica del doppio (a meno che non mi sia sfuggito), quindi entrambi i comportamenti sembrano accettabili ... – assylias

5

il cambiamento di comportamento è documentato in the release notes of Java 8

Quando si utilizzano le classi NumberFormat e DecimalFormat, il ro il comportamento unding delle versioni precedenti del JDK era sbagliato in alcuni casi angusti. [...]

Ad esempio, quando si utilizza il default raccomandato NumberFormatFormat forma API: NumberFormat nf = java.text.NumberFormat.getInstance() seguita da nf.format(0.8055d), il 0.8055d valore è registrato nel computer come 0,80549999999999999378275106209912337362766265869140625 poiché questo valore non può essere rappresentato esattamente nel formato binario. Qui, il valore predefinito di arrotondamento regola è "mezza addirittura", e il risultato della chiamata format() in JDK 7 è una potenza errata di "0,806", mentre il risultato corretto è "0,805", poiché il valore registrato nella memoria dal il computer è "sotto" la cravatta.

Questo nuovo comportamento è implementata anche per tutte le posizioni di arrotondamento che potrebbero essere definita mediante un modello scelto dal programmatore (modelli non predefiniti).