Кратко
- String.format("%.3f", x), а также printf и format у System.out возвращают строку и округляют по правилу HALF_UP: 7.0005 превращается в 7,001.
- new DecimalFormat("#.###") тоже дает строку, но по умолчанию округляет по HALF_EVEN и отбрасывает конечные нули (7.000132 превращается в 7); режим меняется через setRoundingMode().
- Math.ceil() и Math.floor() возвращают double, Math.round() возвращает long (для float на входе int), и все три округляют только до целого; до n знаков число умножают на 10 в степени n, округляют и делят обратно.
- BigDecimal с setScale(3, RoundingMode.DOWN) или new MathContext(5, RoundingMode.HALF_UP) это единственный способ получить число, а не строку, с явно выбранным режимом; старые константы BigDecimal.ROUND_* с Java 9 объявлены устаревшими.
- Режимов RoundingMode восемь (UP, DOWN, CEILING, FLOOR, HALF_UP, HALF_DOWN, HALF_EVEN, UNNECESSARY), а разница между ними видна только на «половинках» вроде 2.5 и на отрицательных числах.
- new BigDecimal(0.1) хранит не 0.1, а 0.1000000000000000055…, поэтому BigDecimal создают из строки или через BigDecimal.valueOf().

String format
В качестве первого способа мы рассмотрим округление double:
double value = 34.766674;
String result = String.format("%.3f",value);
System.out.print(result);// 34,767
В результате мы отформатируем наше число с плавающей запятой 34.766674 с точностью до 3 десятичных разрядов, так как в инструкции для форматирования мы указали три знака после запятой "%.3f".
Запятая в выводе появилась из-за русской локали по умолчанию: разделитель дробной части String.format берет из нее. Чтобы получить точку, передайте локаль первым аргументом: String.format(Locale.US, "%.3f", value) выведет 34.767.
В свою очередь %f при форматировании строки обозначает тип чисел с плавающей запятой, который включает в себя типы данных double и float в Java.
В примере выше мы выводили полученное значение в консоль. Теперь вопрос: как бы можно было это сократить? Все просто: нужно использовать printf, который в свою очередь является format + print. В итоге предыдущий пример у нас сократился бы до:
double value = 34.766674;
System.out.printf("%.3f",value);
У экземпляра out класса PrintStream помимо этого метода есть еще метод format, который работает аналогично:
double value = 34.766674;
System.out.format("%.3f",value);
Округление происходит по режиму HALF_UP: к тому из соседних значений, которое ближе. Если же они равноудалены (отбрасываемая цифра ровно 5), то округление выполняется в большую сторону. Так и записано в документации Formatter, причем округляется не двоичное значение double, а его короткая десятичная запись (та, что выдает Double.toString), поэтому 7.0005 честно превращается в 7,001.
Пример:
String firstResult = String.format("%.3f",7.0004);// 7,000
String secondResult = String.format("%.3f",7.0005);// 7,001
String thirdResult = String.format("%.3f",7.0006);// 7,001
Более подробно режимы округления мы разберем чуть ниже.

DecimalFormat
Еще один вариант: использовать класс DecimalFormat. Он предназначен для форматирования любого числа в Java, будь это целое число или число с плавающей запятой. При создании экземпляра DecimalFormat мы можем передать ему строку форматирования. Она укажет, сколько десятичных разрядов нужно отформатировать для вывода. Как будет выглядеть наш пример с использованием DecimalFormat:
double value = 34.766674;
DecimalFormat decimalFormat = new DecimalFormat( "#.###" );
String result = decimalFormat.format(value);
System.out.print(result);//34,767
Строка #.### это шаблон, который указывает, что мы форматируем передаваемое значение до 3 десятичных знаков.
Чтобы изменить шаблон после создания объекта DecimalFormat, можно использовать его методы applyPattern и applyLocalizedPattern:
DecimalFormat decimalFormat = new DecimalFormat("#.###");
decimalFormat.applyPattern("#.#");
decimalFormat.applyLocalizedPattern("#.#####");
Но мы сегодня говорим про округление, не правда ли? При обрезании числа со знаками после запятой, выходящими за заданный шаблон, DecimalFormat округляет число в большую сторону, если первая отбрасываемая цифра больше 5. Но что, если она равна 5? Получается, что оно ровно посередине между ближайшими целыми. Что тогда?
В этом случае DecimalFormat смотрит на предыдущую цифру и округляет так, чтобы она стала четной. Это режим HALF_EVEN, его еще называют банковским округлением.
Если предыдущая цифра уже четная, число остается как есть:
DecimalFormat decimalFormat = new DecimalFormat("#.##");
String result = decimalFormat.format(7.125);
System.out.println((result));// 7,12
Если нечетная, округляется вверх:
DecimalFormat decimalFormat = new DecimalFormat("#.##");
String result = decimalFormat.format(7.375);
System.out.println((result));// 7,38
Числа в примерах выбраны не случайно: 7.125 и 7.375 хранятся в double точно (это 7 + 1/8 и 7 + 3/8), поэтому «половинка» здесь настоящая. А вот, скажем, 7.4565 в двоичном виде превращается в 7.45650000000000012…, то есть чуть больше половины, и с Java 8 DecimalFormat это учитывает (исправление JDK-7131459): такое число округлится до 7,457 при любой четности предыдущей цифры.
Режим округления у DecimalFormat можно поменять методом setRoundingMode (он есть с Java 6): decimalFormat.setRoundingMode(RoundingMode.HALF_UP) заставит его округлять так же, как String.format.
Есть небольшая разница между форматированием чисел с плавающей запятой с использованием String.format() и DecimalFormat.format(). Первый всегда будет печатать конечные нули, даже если нет дробной части.
К примеру:
String firstResult = String.format("%.3f", 7.000132);
System.out.println((firstResult)); // 7,000
DecimalFormat decimalFormat = new DecimalFormat("#.###");
String secondResult = decimalFormat.format(7.000132);
System.out.println((secondResult)); // 7
Как мы видим, при форматировании числа 7.000132 с точностью до трех десятичных разрядов, метод format() у String выводит 7,000, в то время как у DecimalFormat метод format() выведет 7.
То есть вы можете выбирать String.format() или DecimalFormat.format() в зависимости от того, нужны ли вам конечные нули или нет.
Применяя вышеописанные способы, мы получали результат в виде строки. Давайте же рассмотрим способы, как можно получить обратно именно числовые значения.
Math
Нельзя не упомянуть специальный класс, заточенный под разные арифметические операции с числами: Math (обзор его методов есть в отдельной статье).
В этом классе есть и методы для округления, но в отличие от уже описанных, они не позволяют задать определенное число знаков после запятой, а округляют до целого числа:
Math.ceil() округляет до ближайшего целого числа вверх, но отдает не целочисленный тип, а double:
double value = 34.777774; double result = Math.ceil(value); System.out.println((result)); //35.0Даже если у нас будет 34.00000001, все равно после использования Math.ceil мы получим 35,0.
Math.floor() округляет до ближайшего целого вниз, также результатом отдает double:
double value = 34.777774; double result = Math.floor(value); System.out.println((result)); //34.0Опять же, даже если у нас будет значение 34.999999999, то после использования Math.floor мы получим 34,0.
Math.round() округляет до ближайшего целого числа, но результат отдает не int, а long (int будет только для float на входе):
double value = 34.777774; long result = Math.round(value); System.out.println((result)); //35Если у нас число 34.5, округление выполняется до 35, если же чуть-чуть меньше, например 34.499, число обрезается до 34.
Чтобы не просто обрезать всю вещественную часть, а регулировать этот процесс до определенного количества знаков и при этом округлять, число умножают на 10^n (10 в степени n), где n равно количеству необходимых знаков после запятой. После этого применяют какой-нибудь метод класса Math для округления, ну а затем снова делят на 10^n:
double value = 34.777774; double scale = Math.pow(10, 3); double result = Math.ceil(value * scale) / scale; System.out.println((result)); //34.778Math.pow принимает два аргумента: первый это число, второй это степень, в которую его нужно возвести.
Округление с BigDecimal
BigDecimal это класс, который позволяет работать с десятичными дробями произвольной точности. В частности, его основной фишкой является то, что в нем можно хранить дробные числа произвольной длины (то есть нет ограничения в диапазоне числа). Помимо этого, этот класс хранит различные методы для арифметической обработки, включая округление. Объект этого класса можно создать, передав в конструктор double, строковое представление числа, double вместе с MathContext и так далее. Только осторожно с конструктором от double: new BigDecimal(0.1) создаст не 0.1, а 0.1000000000000000055511151231257827…, потому что 0.1 в двоичном виде точно не представимо (почему так устроены вещественные числа, разобрано в отдельной статье). Документация Oracle прямо советует строковый конструктор new BigDecimal("0.1") или BigDecimal.valueOf(0.1), который берет короткую запись числа из Double.toString. Поэтому в примерах ниже число передается в конструктор строкой. MathContext это комбинация правила округления RoundingMode и числа, задающего общее количество значащих цифр в результате. Правила округления RoundingMode (в третьей колонке показано, во что превращаются 2.5, 2.4 и -2.5 при округлении до целого):| Режим | Правило | 2.5 / 2.4 / -2.5 |
|---|---|---|
| UP | от нуля: любая ненулевая отбрасываемая часть увеличивает последнюю цифру | 3 / 3 / -3 |
| DOWN | к нулю: отбрасываемая часть просто отсекается | 2 / 2 / -2 |
| CEILING | в сторону положительной бесконечности | 3 / 3 / -2 |
| FLOOR | в сторону отрицательной бесконечности | 2 / 2 / -3 |
| HALF_UP | к ближайшему соседу; если оба соседа равноудалены (отбрасывается ровно 5), то от нуля. Так учат округлять в школе | 3 / 2 / -3 |
| HALF_DOWN | к ближайшему соседу; если оба соседа равноудалены, то к нулю | 2 / 2 / -2 |
| HALF_EVEN | к ближайшему соседу; если оба соседа равноудалены, то к тому, у которого последняя цифра четная («банковское округление», именно так по умолчанию работает описанный выше DecimalFormat) | 2 / 2 / -2 |
| UNNECESSARY | утверждает, что результат точный и округлять ничего не нужно; если округление все же потребовалось, бросает ArithmeticException | исключение во всех трех случаях |
MathContext context = new MathContext(5, RoundingMode.HALF_UP);
BigDecimal result = new BigDecimal("34.777554", context);
System.out.println(result); //34.778
Помимо возможности задавать правило округления в конструкторе, BigDecimal позволяет задать режим округления уже после создания экземпляра. Для этого используется метод setScale, в который нужно задать количество знаков после запятой и правила округления:
BigDecimal result = new BigDecimal("34.777554");
result = result.setScale(3, RoundingMode.DOWN);
System.out.println(result); //34.777
В старом коде можно встретить и целочисленные константы BigDecimal для задания режима округления (ROUND_DOWN, ROUND_CEILING, ROUND_FLOOR...).
Это те же правила, что в классе RoundingMode, и раньше их точно так же передавали в setScale:
BigDecimal result = new BigDecimal("34.777554");
result = result.setScale(3, BigDecimal.ROUND_DOWN);
С Java 9 и сами константы, и перегрузка setScale(int, int) помечены как @Deprecated: документация отсылает к setScale(int, RoundingMode). В новом коде используйте только RoundingMode, а этот вариант держите в голове, чтобы узнавать его в чужих проектах.
Подробнее о классе BigDecimal в этой статье.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ