2016-05-13 6 views
1

Почему это DecimalFormat не округляет, используя RoundingMode.HALF_UP, как и ожидалось, и что делать, чтобы получить ожидаемый результат? Можно ли это сделать с DecimalFormat?DecimalFormat, используя неверный RoundingMode, даже если он установлен явно

DecimalFormat df = new DecimalFormat("0.0"); 

df.setRoundingMode(RoundingMode.HALF_UP); 

df.format(0.05); // expecting 0.1, getting 0.1 
df.format(0.15); // expecting 0.2, getting 0.1 << unexpected 

df.format(0.06); // expecting 0.1, getting 0.0 << unexpected 

Я видел ответы от this question, в частности this answer, но это только кажется, работает, когда округление до целого числа.

Мой JDK является 8.0.110 и мой JRE является 8.0.730.2

+1

это _is_ таинственный! – tofutim

+0

возможно, вам придется умножить на 10 и снова разделить на обход – tofutim

+1

Работы отлично в 1.7 возможно см. Http://stackoverflow.com/questions/22797964/is-inconsistency-in-rounding-between-java-7 -and-java-8-a-bug –

ответ

3

(Ответ ниже с использованием Java 8.)

Проблема вы видите происходит от определения «0,15» или «0,05» в коде , который, будучи представленным как двойной, немного меньше 0,15. Проверьте это

DecimalFormat df = new DecimalFormat("#.#"); 

df.setRoundingMode(RoundingMode.HALF_UP); 

BigDecimal bd = new BigDecimal(0.15); 
System.out.println("bd=" + bd); 
System.out.println(df.format(0.15)); // expecting 0.1, getting 0.1 
bd = new BigDecimal(0.05); 
System.out.println("bd=" + bd); 
System.out.println(df.format(0.05)); 
bd = new BigDecimal(0.06); 
System.out.println("bd=" + bd); 
System.out.println(df.format(0.06)); 

Вывод этого кода

bd=0.1499999999999999944488848768742172978818416595458984375 
0.1 
bd=0.05000000000000000277555756156289135105907917022705078125 
0.1 
bd=0.059999999999999997779553950749686919152736663818359375 
0.1 

Возможное решение (если это абсолютно необходимо, чтобы закруглить правильный путь), чтобы использовать BigDecimal.valueOf для создания ценности. Например

BigDecimal bd = BigDecimal.valueOf(0.15); 
System.out.println("bd=" + bd); 
System.out.println(df.format(bd)); // expecting 0.1, getting 0.1 
bd = BigDecimal.valueOf(0.05); 
System.out.println("bd=" + bd); 
System.out.println(df.format(bd)); 
bd = BigDecimal.valueOf(0.06); 
System.out.println("bd=" + bd); 
System.out.println(df.format(bd)); 

Будет теперь уступают

bd=0.15 
0.2 
bd=0.05 
0.1 
bd=0.06 
0.1 

BTW, так как Scary Вомбат указал, маска устанавливается как 0.0 вместо #. # Составит 0,6 0. Но я думаю, что было позже редактировать затем, когда я начал смотреть на него. Используйте #. #.

+1

Почему downvote? :( – tofutim

+0

Итак, проблема заключается в 'DecimalFormat df = new DecimalFormat (" 0.0 ");' vs 'DecimalFormat df = new DecimalFormat (" #. # ");'? –

+0

@ScaryWombat Он этого не сказал. взятие около? – EJP

4

Когда вы

df.format(0.15); 

есть на самом деле две операции округления. Очевидным является тот, который вы попросили с df.format, но есть более ранний, который случается во время компиляции.

Десятичное значение 0,15 не представляется двойным. Партии могут представлять только рационалисты, знаменатель которых является степенью двух, так же как десятичное обозначение может представлять только рациональные числа, знаменатель которых равен десяти. Когда вы пишете 0.15, компилятор раундов это до ближайшего значения, представимых в виде двойной, и это значение случается

0.1499999999999999944488848768742172978818416595458984375 

который df.format правильно округляет вниз. Это не совсем на полпути между 0,1 и 0,2, поэтому режим округления HALF_UP не имеет значения.

Что касается

df.format(0.06); // expecting 0.1, getting 0.0 << unexpected 

Если вы видите, что , что это ошибка. Похоже, что он соответствует one linked by ScaryWombat, сообщается, что он исправлен в 8u40 и 8u45. Тест на Ideone, который использует 8u51, показывает правильное поведение. Обновление вашей Java должно решить проблему.

+3

Мне это нравится: «двойники могут представлять только рационалисты, знаменатель которых является степенью двух» – tofutim

+1

Можете ли вы затем объяснить «df.format (0.06); // ожидая 0,1, получив 0.0 << неожиданный' - также почему ОК для 1.7, а не 1.8? –

+1

@ user2357112 Вы имели в виду использовать 0.0 ... а не 0.1 ... после первого обращения к примеру 0.05? Кроме того, превосходный ответ. –

0

Я думаю, что ответ IEEE Standard for Floating-Point Arithmetic (IEEE 754). Статья в Вики имеет хороший пример в разделе Rounding rules.Также вы можете прочитать раздел 9.1 of Introduction to Java, который расходует больше информации о плавающей запятой. BigDecimal решает большинство этих коротких замыканий, сохраняя невостребованное значение внутри BigInteger, точность и масштаб отдельно в целочисленных полях.

Смежные вопросы