Проблема RoundingMode.HALF_DOWN в Java8
Я использую jdk 1.8.0_45, и наши тесты обнаружили ошибку в роудинге. RoundingMode.HALF_DOWN работает так же, как RoundingMode.HALF_UP, когда последний десятичный знак, определяющий округление, равен 5.
Я обнаружил связанные с этим проблемы с RoundingMode.HALF_UP, но они исправлены в обновлении 40. Я также добавил баг в оракула, но по моему опыту они действительно не отвечают.
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));
}
}
Фактический результат: округлить 10,5556 округлить 10,5556
Ожидаемый результат (получить с помощью jdk 1.7): округлить в меньшую сторону 10,5555 округлить в большую сторону 10,5556
2 ответа
Кажется, что это намеренное изменение. Поведение JDK 1.7 было неверным.
Проблема в том, что вы просто не можете представить число 10.55555
с использованием double
тип. Он хранит данные в двоичном формате IEEE, поэтому при назначении десятичной 10.55555
номер к double
переменная, вы на самом деле получите ближайшее значение, которое может быть представлено в формате IEEE: 10.555550000000000210320649784989655017852783203125
, Это число больше, чем 10.55555
так что это правильно округлено до 10.5556
в HALF_DOWN
Режим.
Вы можете проверить некоторые числа, которые могут быть точно представлены в двоичном виде. Например, 10.15625
(который 10 + 5/32
таким образом 1010.00101
в двоичном виде). Это число округляется до 10.1562
в HALF_DOWN
режим и 10.1563
в HALF_UP
Режим.
Если вы хотите восстановить старое поведение, вы можете сначала преобразовать свой номер в BigDecimal
с помощью BigDecimal.valueOf
конструктор, который "переводит double
в BigDecimal
, с использованием 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
Изменение поведения задокументировано в заметках о выпуске Java 8
При использовании
NumberFormat
а такжеDecimalFormat
классы, поведение округления предыдущих версий JDK было неправильным в некоторых угловых случаях. [...]В качестве примера при использовании по умолчанию рекомендуется
NumberFormatFormat
Форма API:NumberFormat nf = java.text.NumberFormat.getInstance()
с последующимnf.format(0.8055d)
значение 0.8055d записывается на компьютере как 0.80549999999999999378275106209912337362766265869140625, поскольку это значение не может быть точно представлено в двоичном формате. Здесь правилом округления по умолчанию является "half-even", а результатом вызова format() в JDK 7 является неправильный вывод "0.806", в то время как правильный результат - "0.805", поскольку значение, записанное в памяти компьютер "под" галстуком.Это новое поведение также реализовано для всех позиций округления, которые могут быть определены любым шаблоном, выбранным программистом (шаблоны не по умолчанию).