Практическое руководство по десятичному формату

Практическое руководство по десятичному формату

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.