Перейти к основному содержимому

Форматирование чисел в Java

· 5 мин. чтения

1. Обзор

В этом руководстве мы рассмотрим различные подходы к форматированию чисел в Java и способы их реализации.

2. Базовое форматирование чисел с помощью String#format

Метод String#format очень полезен для форматирования чисел. Метод принимает два аргумента. Первый аргумент описывает образец того, сколько знаков после запятой мы хотим видеть, а второй аргумент — заданное значение:

double value = 4.2352989244d;
assertThat(String.format("%.2f", value)).isEqualTo("4.24");
assertThat(String.format("%.3f", value)).isEqualTo("4.235");

3. Десятичное форматирование путем округления

В Java у нас есть два примитивных типа, которые представляют десятичные числаfloat и decimal :

double myDouble = 7.8723d;
float myFloat = 7.8723f;

Количество знаков после запятой может быть разным в зависимости от выполняемых операций. В большинстве случаев нас интересуют только первые несколько знаков после запятой . Давайте рассмотрим некоторые способы форматирования десятичной дроби путем округления.

3.1. Использование BigDecimal для форматирования чисел

Класс BigDecimal предоставляет методы для округления до указанного числа знаков после запятой. Давайте создадим вспомогательный метод, который будет возвращать двойное число, округленное до нужного числа разрядов:

public static double withBigDecimal(double value, int places) {
BigDecimal bigDecimal = new BigDecimal(value);
bigDecimal = bigDecimal.setScale(places, RoundingMode.HALF_UP);
return bigDecimal.doubleValue();
}

Мы начинаем с нового экземпляра BigDecimal с нашим исходным десятичным значением. Затем, устанавливая масштаб, мы указываем желаемое количество знаков после запятой и то, как мы хотим округлить число . Использование этого метода позволяет нам легко форматировать двойное значение:

double D = 4.2352989244d;
assertThat(withBigDecimal(D, 2)).isEqualTo(4.24);
assertThat(withBigDecimal(D, 3)).isEqualTo(4.235);

3.2. Использование Math#round

Мы также можем воспользоваться статическими методами класса Math , чтобы округлить двойное значение до указанного десятичного разряда. В этом случае мы можем настроить количество знаков после запятой, умножив, а затем разделив на 10^n . Давайте проверим наш вспомогательный метод:

public static double withMathRound(double value, int places) {
double scale = Math.pow(10, places);
return Math.round(value * scale) / scale;
}
assertThat(withMathRound(D, 2)).isEqualTo(4.24);
assertThat(withMathRound(D, 3)).isEqualTo(4.235);

Однако этот способ рекомендуется только в особых случаях, так как иногда вывод может округляться не так, как ожидалось перед печатью.

Это связано с тем, что Math#round усекает значение . Давайте посмотрим, как это может произойти:

System.out.println(withMathRound(1000.0d, 17));
// Gives: 92.23372036854776 !!
System.out.println(withMathRound(260.775d, 2));
// Gives: 260.77 instead of expected 260.78

Итак, метод указан только для учебных целей.

4. Форматирование различных типов чисел

В некоторых конкретных случаях нам может понадобиться отформатировать число для определенного типа, такого как валюта, большое целое число или процент.

4.1. Форматирование больших целых чисел с помощью запятых

Всякий раз, когда у нас есть большое целое число в нашем приложении, мы можем захотеть отобразить его с запятыми, используя DecimalFormat с предопределенным шаблоном:

public static String withLargeIntegers(double value) {
DecimalFormat df = new DecimalFormat("###,###,###");
return df.format(value);
}

int value = 123456789;
assertThat(withLargeIntegers(value)).isEqualTo("123,456,789");

4.2. Дополнение числа

В некоторых случаях мы можем захотеть дополнить число нулями для заданной длины. Здесь мы можем использовать метод String#format , как описано ранее:

public static String byPaddingZeros(int value, int paddingLength) {
return String.format("%0" + paddingLength + "d", value);
}

int value = 1;
assertThat(byPaddingOutZeros(value, 3)).isEqualTo("001");

4.3. Форматирование чисел с двумя нулями после запятой

Чтобы иметь возможность печатать любое заданное число с двумя нулями после запятой, мы будем использовать еще один класс DecimalFormat с предопределенным шаблоном:

public static double withTwoDecimalPlaces(double value) {
DecimalFormat df = new DecimalFormat("#.00");
return new Double(df.format(value));
}

int value = 12;
assertThat(withTwoDecimalPlaces(value)).isEqualTo(12.00);

В данном случае мы создали новый формат с шаблоном, указывающим два нуля после запятой .

4.4. Форматирование и проценты

Время от времени нам может понадобиться отображать проценты.

В этом случае мы можем использовать метод NumberFormat# getPercentInstance . Этот метод позволяет нам предоставить Locale для печати значения в формате, правильном для указанной вами страны:

public static String forPercentages(double value, Locale locale) {
NumberFormat nf = NumberFormat.getPercentInstance(locale);
return nf.format(value);
}

double value = 25f / 100f;
assertThat(forPercentages(value, new Locale("en", "US"))).isEqualTo("25%");

4.5. Форматирование номера валюты

Обычный способ хранения валют в нашем приложении — использование BigDecimal . Что, если мы хотим отобразить их пользователю? В этом случае мы можем использовать класс NumberFormat :

public static String currencyWithChosenLocalisation(double value, Locale locale) {
NumberFormat nf = NumberFormat.getCurrencyInstance(locale);
return nf.format(value);
}

Мы получаем экземпляр валюты для данной локали , а затем просто вызываем метод форматирования со значением. Результатом является число, отображаемое в качестве валюты для указанной страны:

double value = 23_500;
assertThat(currencyWithChosenLocalisation(value, new Locale("en", "US"))).isEqualTo("$23,500.00");
assertThat(currencyWithChosenLocalisation(value, new Locale("zh", "CN"))).isEqualTo("¥23,500.00");
assertThat(currencyWithChosenLocalisation(value, new Locale("pl", "PL"))).isEqualTo("23 500 zł");

5. Варианты использования расширенного форматирования

DecimalFormat — один из самых популярных способов форматирования десятичного числа в Java. Как и в предыдущих примерах, мы напишем вспомогательный метод:

public static double withDecimalFormatLocal(double value) {
DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.getDefault());
return new Double(df.format(value));
}

Наш тип форматирования получит настройку по умолчанию для данной локализации.

Десятичное форматирование обрабатывается по-разному в разных странах, использующих свои системы счисления. Например, символ группировки (запятая в США, но пробел или точка в других регионах), размер группы (три в США и большинстве регионов, но разные в Индии) или десятичный символ (точка в США, но запятая в других регионах).

double D = 4.2352989244d;
assertThat(withDecimalFormatLocal(D)).isEqualTo(4.235);

Мы также можем расширить эту функциональность, чтобы предоставить некоторые определенные шаблоны:

public static double withDecimalFormatPattern(double value, int places) {
DecimalFormat df2 = new DecimalFormat("#,###,###,##0.00");
DecimalFormat df3 = new DecimalFormat("#,###,###,##0.000");
if (places == 2)
return new Double(df2.format(value));
else if (places == 3)
return new Double(df3.format(value));
else
throw new IllegalArgumentException();
}

assertThat(withDecimalFormatPattern(D, 2)).isEqualTo(4.24);
assertThat(withDecimalFormatPattern(D, 3)).isEqualTo(4.235);

Здесь мы позволяем нашему пользователю настроить DecimalFormat по выбранному шаблону на основе количества пробелов.

6. Заключение

В этой статье мы кратко рассмотрели различные способы форматирования чисел в Java. Как мы видим, лучшего способа сделать это не существует. Можно использовать множество подходов, поскольку каждый из них имеет свои особенности.

Как всегда, код этих примеров доступен на GitHub .