Практическое руководство по десятичному формату
1. обзор
В этой статье мы собираемся изучить классDecimalFormat и его практическое использование.
Это подклассNumberFormat, который позволяет форматировать представление десятичных чиселString с использованием предопределенных шаблонов.
Это также может быть использовано в обратном порядке, чтобы разобрать строки в числа.
2. Как это работает?
Чтобы отформатировать число, мы должны определить шаблон, который представляет собой последовательность специальных символов, потенциально смешанных с текстом.
Есть 11 специальных символов шаблона, но наиболее важными являются:
-
0 - печатает цифру, если есть, 0 в противном случае
-
# - печатает цифру, если есть, иначе ничего
-
, - указать, где поставить десятичный разделитель
-
, - указать, куда поместить разделитель группировки
Когда шаблон применяется к числу, его правила форматирования выполняются, и результат печатается в соответствии сDecimalFormatSymbol нашей JVMLocale, если не указан конкретныйLocale.
В следующих примерах выводится JVM, работающая на английском языкеLocale.
3. Основное форматирование
Давайте теперь посмотрим, какие выходные данные получаются при форматировании одного и того же числа с помощью следующих шаблонов.
3.1. Простые десятичные числа
double d = 1234567.89;
assertThat(
new DecimalFormat("#.##").format(d)).isEqualTo("1234567.89");
assertThat(
new DecimalFormat("0.00").format(d)).isEqualTo("1234567.89");
Как мы видим, целочисленная часть никогда не отбрасывается, независимо от того, меньше ли шаблон числа.
assertThat(new DecimalFormat("#########.###").format(d))
.isEqualTo("1234567.89");
assertThat(new DecimalFormat("000000000.000").format(d))
.isEqualTo("001234567.890");
Если вместо этого шаблон больше, чем число, добавляются нули, а хэши удаляются как в целых, так и в десятичных частях.
3.2. округление
Если десятичная часть шаблона не может содержать всю точность введенного числа, она округляется.
Здесь, часть .89 была округлена до .90, затем 0 был удален:
assertThat(new DecimalFormat("#.#").format(d))
.isEqualTo("1234567.9");
Здесь, часть 0,89 была округлена до 1,00, затем 0,00 была отброшена, а 1 была суммирована с 7:
assertThat(new DecimalFormat("#").format(d))
.isEqualTo("1234568");
По умолчанию используется режим округленияHALF_EVEN,, но его можно настроить с помощью методаsetRoundingMode.
3.3. группирование
Разделитель группировки используется для указания подшаблона, который повторяется автоматически:
assertThat(new DecimalFormat("#,###.#").format(d))
.isEqualTo("1,234,567.9");
assertThat(new DecimalFormat("#,###").format(d))
.isEqualTo("1,234,568");
3.4. Множественные шаблоны группировки
Некоторые страны имеют различное количество группировок в своих системах нумерации.
Индийская система нумерации использует формат,,. ##, в котором только первый разделитель группировки содержит три числа, а все остальные - два числа.
Этого невозможно добиться с помощью классаDecimalFormat, который сохраняет только последний шаблон, встреченный слева направо, и применяет его ко всему числу, игнорируя предыдущие шаблоны группирования.
Попытка использовать шаблон,,,, приведет к перегруппировке в, и завершится перераспределением в,,, ##.
Чтобы добиться совпадения множественных шаблонов группировки, необходимо написать наш собственный код манипуляцииString или, альтернативно, попробоватьIcu4J’s DecimalFormat, который позволяет это.
3.5. Смешивание строковых литералов
В шаблоне можно смешивать литералыString:
assertThat(new DecimalFormat("The # number")
.format(d))
.isEqualTo("The 1234568 number");
Также можно использовать специальные символы в качестве литераловString, используя экранирование:
assertThat(new DecimalFormat("The '#' # number")
.format(d))
.isEqualTo("The # 1234568 number");
4. Локализованное форматирование
Многиеcountries не используют английские символы и используют запятую в качестве десятичного разделителя, а точку - в качестве разделителя группировки.
Например, запуск шаблона,. на JVM с итальянскимLocale выдаст 1.234.567,89.
Хотя в некоторых случаях это может быть полезной функцией i18n, в других может потребоваться применение определенного, независимого от JVM формата.
Вот как мы можем это сделать:
assertThat(new DecimalFormat("#,###.##",
new DecimalFormatSymbols(Locale.ENGLISH)).format(d))
.isEqualTo("1,234,567.89");
assertThat(new DecimalFormat("#,###.##",
new DecimalFormatSymbols(Locale.ITALIAN)).format(d))
.isEqualTo("1.234.567,89");
Если интересующий насLocale не входит в число тех, которые охватываются конструкторомDecimalFormatSymbols, мы можем указать его с помощью методаgetInstance:
Locale customLocale = new Locale("it", "IT");
assertThat(new DecimalFormat(
"#,###.##",
DecimalFormatSymbols.getInstance(customLocale)).format(d))
.isEqualTo("1.234.567,89");
5. Научные обозначения
Scientific Notation представляет собой произведение мантиссы и показателя десяти. Число 1234567.89 также может быть представлено как 12.3456789 * 10 ^ 5 (точка смещена на 5 позиций).
5.1. E-обозначение
Число в научной нотации можно выразить с помощью символа шаблонаE, представляющего показатель степени десяти:
assertThat(new DecimalFormat("00.#######E0").format(d))
.isEqualTo("12.3456789E5");
assertThat(new DecimalFormat("000.000000E0").format(d))
.isEqualTo("123.456789E4");
Мы должны иметь в виду, что количество символов после экспоненты имеет значение, поэтому, если нам нужно выразить 10 ^ 12, нам понадобитсяE00, а неE0.
5.2. Инженерное обозначение
Обычно используется особая форма научной нотации, называемая инженерной нотацией, которая корректирует результаты, чтобы они были выражены как кратные трем, например, при использовании таких единиц измерения, как Кило (10 ^ 3), Мега (10 ^ 6), Гига ( 10 ^ 9) и так далее.
Мы можем обеспечить соблюдение такого рода обозначений, отрегулировав максимальное количество целых цифр (символы, выраженные с помощью # и слева от десятичного разделителя), чтобы оно было больше минимального числа (выраженного с помощью 0) и больше, чем 1.
Это приводит к тому, что показатель степени будет кратным максимальному числу, поэтому для этого варианта использования мы хотим, чтобы максимальное число равнялось трем:
assertThat(new DecimalFormat("##0.######E0")
.format(d)).isEqualTo("1.23456789E6");
assertThat(new DecimalFormat("###.000000E0")
.format(d)).isEqualTo("1.23456789E6");
6. анализ
Давайте посмотрим, как можно преобразоватьString вNumber с помощью метода синтаксического анализа:
assertThat(new DecimalFormat("", new DecimalFormatSymbols(Locale.ENGLISH))
.parse("1234567.89"))
.isEqualTo(1234567.89);
assertThat(new DecimalFormat("", new DecimalFormatSymbols(Locale.ITALIAN))
.parse("1.234.567,89"))
.isEqualTo(1234567.89);
Поскольку возвращаемое значение не определяется наличием десятичного разделителя, мы можем использовать такие методы, как.doubleValue(),.longValue() возвращаемого объектаNumber, чтобы обеспечить вывод определенного примитива.
Мы также можем получить aBigDecimal следующим образом:
NumberFormat nf = new DecimalFormat(
"",
new DecimalFormatSymbols(Locale.ENGLISH));
((DecimalFormat) nf).setParseBigDecimal(true);
assertThat(nf.parse("1234567.89"))
.isEqualTo(BigDecimal.valueOf(1234567.89));
7. Потокобезопасность
DecimalFormat isn’t thread-safe, поэтому нам следует уделять особое внимание при совместном использовании одного и того же экземпляра между потоками.
8. Заключение
Мы видели основные примеры использования классаDecimalFormat, а также его сильные и слабые стороны.
Как всегда, доступен полный исходный кодover on Github.