Округление чисел в Java

Статья из группы Java Developer
Числа с плавающей точкой (float, double) применяются при вычислении выражений, в которых требуется точность до десятичного знака. Высокая точность часто нужна в бухгалтерских и других вычислительных операциях. Округление чисел в Java - 1Но всегда ли нам нужен длинный “хвост” чисел после запятой? Может нам достаточно точности в три знака вещественной части? И есть нас такой вариант устраивает, как правильно выполнить округление? Именно об этом мы сегодня и поговорим: рассмотрим способы округления чисел в Java.

String format

В качестве первого способа мы рассмотрим округление double:

double value = 34.766674;
String result = String.format("%.3f",value);
System.out.print(result);//  34,767
В результате мы отформатируем наше число с плавающей запятой 34766674 с точностью до 3 десятичных разрядов, так как в инструкции для форматирования мы указали три знака после запятой "%.3f. В свою очередь %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 — в сторону числа, которое ближе к обрезаемому (к 0 или 10). Если же эти числа равноудалены (в случае с 5), то округление выполняется в большую сторону. Пример:

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
Более подробно режимы округления мы разберем чуть ниже. Округление чисел в Java - 2

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 decimalFormat = new DecimalFormat("#.###");
String result = decimalFormat.format(7.4565);
System.out.println((result));// 7,457
Если нечётное — не производится:

DecimalFormat decimalFormat = new DecimalFormat("#.###");
String result = decimalFormat.format(7.4575);
System.out.println((result));// 7,457
Есть небольшая разница между форматированием чисел с плавающей запятой с использованием 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. Округление чисел в Java - 3В этом классе есть и методы для округления, но в отличие от уже описанных, они не позволяют задать определенное число знаков после запятой, а округляют до целого числа:
  • Math.ceil() округляет до ближайшего целого числа вверх, но отдаёт не целочисленный тип, а double:

    
    double value = 34.777774;
    double result = Math.ceil(value);
    System.out.println((result)); //35.0
    

    Даже если у нас будет 34.0000000, все равно после использования 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:

    
    double value = 34.777774;
    int 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.778
    

    Math.pow — принимает два аргумента. Первый — число, второй — степень, в которую его нужно возвести.

Округление чисел в Java - 4

Округление с BigDecimal

BigDecimal — это класс, который позволяет работать с числами с плавающей запятой. В частности, его основной фишкой является то, что в нём можно хранить дробные числа произвольной длины (то есть нет ограничения в диапазоне числа). Помимо этого, этот класс хранит различные методы для арифметической обработки, включая округление. Класс данного объекта можно создать, задав в конструктор double, string отображение числа с плавающей запятой, double и MathContext и так далее. MathContext — комбинация правила округления RoundingMode и числа, описывающая общее количество цифр в искомом значении. Правила округления RoundingMode: DOWN — округление в сторону до нуля. UP — режим округления от нуля. CEILING — округление в сторону положительной бесконечности. FLOOR — округление в сторону к отрицательной бесконечности. HALF_UP — округление до «ближайшего соседа», если оба соседа не равноудалены (то есть когда округляемое число — 5). В этом случае выполняется округление вверх. HALF_DOWN — округление в сторону «ближайшего соседа». Если оба соседа не равноудалены, в этом случае округление вниз. HALF_EVEN — округление до «ближайшего соседа», если оба соседа не равноудалены. В этом случае округляются до четного соседа (как в описанном выше DecimalFormat). UNNECESSARY — используется для подтверждения того, что запрошенная операция имеет точный результат. Следовательно, округление не требуется. Пример:

MathContext context = new MathContext(5, RoundingMode.HALF_UP);
double value = 34.777554;
BigDecimal result = new BigDecimal(value, context);
System.out.println(result); //34.778
Помимо возможности задавать правило округления в конструкторе, BigDecimal позволяет задать режим округления уже после создания экземпляра. Для этого используется метод setScale, в который нужно задать количество знаков после запятой и правила округления:

double value = 34.777554;
BigDecimal result = new BigDecimal(value);
result = result.setScale(3, RoundingMode.DOWN);
System.out.println(result); //34.777
Также у BigDecimal есть внутренние int переменные, предназначенные для задания режима округления (ROUND_DOWN, ROUND_CEILING, ROUND_FLOOR...) Представляют собой правила округления, аналогичные представленным в классе RoundingMode и аналогично используются в setScale:

BigDecimal result = new BigDecimal(value);
result = result.setScale(3, BigDecimal.ROUND_DOWN);
Подробнее о классе BigDecimal — в этой статье.
Комментарии (8)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Алексей Мирный Уровень 41, Москва, Россия
17 января 2022
У String format и DecimalFormat есть одна маленькая большая проблема, внимательные заметили, что при создании строки точка меняется на запятую, те при попытке распарсить эту строку в дабл вы получите эксепшен(
Андрей Овчаренко Уровень 41, Москва
23 января 2021
В статье упущен важный момент - некоторые методы в результате округления ставят точку, а некоторые запятую!
Sergey F. Уровень 25, США
16 июня 2020

double value = 34.777774;
double result = Math.ceil(value);
System.out.println((result)); //35.0
Даже если у нас будет 34.0000000, все равно после использования Math.ceil мы получим 35,0. Возможно я не так понял, но код

double value = 34.0000000;
double result = Math.ceil(value);
System.out.println(result);
выдаст результат:

34.0
AButrym Уровень 1, Харьков
11 июня 2020

double value = 34.777774;
int result = Math.round(value);
System.out.println((result));
Здесь не скомпилируется - Math.round(double) возвращает long.
AButrym Уровень 1, Харьков
11 июня 2020
"Даже если у нас будет 34.0000000, все равно после использования Math.ceil мы получим 35,0." Здесь, по-видимому, опечатка, необходима хотя бы одна цифра после нулей, чтобы округлялось до 35. Стоит ещё упомянуть код формата `g`, который позволяет задавать кол-во значащих цифр:

System.out.format("%.5g%n", 77.0); // => 77.000