Un guide pratique pour DecimalFormat

Un guide pratique pour DecimalFormat

1. Vue d'ensemble

Dans cet article, nous allons explorer la classeDecimalFormat ainsi que ses utilisations pratiques.

Il s'agit d'une sous-classe deNumberFormat, qui permet de formater la représentationStringdes nombres décimaux en utilisant des modèles prédéfinis.

Il peut également être utilisé inversement pour analyser des chaînes en nombres.

2. Comment ça marche?

Pour formater un nombre, nous devons définir un motif, qui est une séquence de caractères spéciaux potentiellement mélangés avec du texte.

Il y a 11 caractères spéciaux, mais les plus importants sont:

  • 0 - imprime un chiffre si fourni, 0 sinon

  • # - imprime un chiffre si fourni, rien d'autre

  • . - indiquer où mettre le séparateur décimal

  • , - indique où placer le séparateur de regroupement

Lorsque le modèle est appliqué à un nombre, ses règles de formatage sont exécutées et le résultat est imprimé selon lesDecimalFormatSymbol de notre JVMLocale à moins qu’unLocale spécifique ne soit spécifié.

Les sorties des exemples suivants proviennent d'une machine virtuelle Java exécutée sur unLocale anglais.

3. Formatage de base

Voyons maintenant quelles sorties sont produites lors du formatage du même nombre avec les modèles suivants.

3.1. Décimales simples

double d = 1234567.89;
assertThat(
  new DecimalFormat("#.##").format(d)).isEqualTo("1234567.89");
assertThat(
  new DecimalFormat("0.00").format(d)).isEqualTo("1234567.89");

Comme on peut le constater, la partie entière n’est jamais ignorée, que le motif soit plus petit que le nombre.

assertThat(new DecimalFormat("#########.###").format(d))
  .isEqualTo("1234567.89");
assertThat(new DecimalFormat("000000000.000").format(d))
  .isEqualTo("001234567.890");

Si le motif est plus grand que le nombre, les zéros sont ajoutés, tandis que les hachages sont supprimés, à la fois dans les parties entière et décimale.

3.2. Arrondi

Si la partie décimale du motif ne peut pas contenir toute la précision du nombre saisi, elle est arrondie.

Ici, la partie .89 a été arrondie à .90, puis le 0 a été supprimé:

assertThat(new DecimalFormat("#.#").format(d))
  .isEqualTo("1234567.9");

Ici, la partie 0,89 a été arrondie à 1,00, puis la valeur 0,00 a été supprimée et le 1 a été additionné à 7:

assertThat(new DecimalFormat("#").format(d))
  .isEqualTo("1234568");

Le mode d'arrondi par défaut estHALF_EVEN, mais il peut être personnalisé via la méthodesetRoundingMode.

3.3. Regroupement

Le séparateur de groupement est utilisé pour spécifier un sous-motif qui se répète automatiquement:

assertThat(new DecimalFormat("#,###.#").format(d))
  .isEqualTo("1,234,567.9");
assertThat(new DecimalFormat("#,###").format(d))
  .isEqualTo("1,234,568");

3.4. Modèles de regroupement multiples

Certains pays ont un nombre variable de modèles de regroupement dans leurs systèmes de numérotation.

Le système de numérotation indien utilise le format,,. ##, dans lequel seul le premier séparateur de regroupement contient trois nombres, tandis que tous les autres contiennent deux nombres.

Cela n’est pas possible en utilisant la classeDecimalFormat, qui ne conserve que le dernier modèle rencontré de gauche à droite et l’applique au nombre entier, en ignorant les modèles de regroupement précédents.

Une tentative d'utilisation du modèle,,,, entraînerait un regroupement vers, et se terminerait par une redistribution vers,,, ##.

Pour obtenir une correspondance de modèles de regroupement multiple, il est nécessaire d’écrire notre propre code de manipulationString, ou d’essayer lesIcu4J’s DecimalFormat, ce qui le permet.

3.5. Mélange de littéraux de chaîne

Il est possible de mélanger les littérauxString dans le modèle:

assertThat(new DecimalFormat("The # number")
  .format(d))
  .isEqualTo("The 1234568 number");

Il est également possible d’utiliser des caractères spéciaux comme littérauxString, en échappant:

assertThat(new DecimalFormat("The '#' # number")
  .format(d))
  .isEqualTo("The # 1234568 number");

4. Formatage localisé

De nombreuxcountriesn'utilisent pas de symboles anglais et utilisent la virgule comme séparateur décimal et le point comme séparateur de groupe.

L'exécution du modèle,. sur une machine virtuelle Java avec unLocale italien, par exemple, produirait 1.234.567,89.

Bien que cela puisse être une fonctionnalité utile d’i18n dans certains cas, dans d’autres, nous voudrons peut-être appliquer un format spécifique, indépendant de JVM.

Voici comment nous pouvons y parvenir:

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");

Si leLocale qui nous intéresse ne fait pas partie de ceux couverts par le constructeurDecimalFormatSymbols, nous pouvons le spécifier avec la méthodegetInstance:

Locale customLocale = new Locale("it", "IT");
assertThat(new DecimalFormat(
  "#,###.##",
   DecimalFormatSymbols.getInstance(customLocale)).format(d))
  .isEqualTo("1.234.567,89");

5. Notations scientifiques

LeScientific Notation représente le produit d'une mantisse et d'un exposant de dix. Le nombre 1234567.89 peut également être représenté par 12.3456789 * 10 ^ 5 (le point est décalé de 5 positions).

5.1. NotationE

Il est possible d’exprimer un nombre en notation scientifique en utilisant le caractère de modèleE représentant l’exposant de dix:

assertThat(new DecimalFormat("00.#######E0").format(d))
  .isEqualTo("12.3456789E5");
assertThat(new DecimalFormat("000.000000E0").format(d))
  .isEqualTo("123.456789E4");

Nous devons garder à l'esprit que le nombre de caractères après l'exposant est pertinent, donc si nous devons exprimer 10 ^ 12, nous avons besoin deE00 et non deE0.

5.2. Notation d'ingénierie

Il est courant d'utiliser une forme particulière de notation scientifique appelée Notation d'ingénierie, qui ajuste les résultats afin d'être exprimés sous forme de multiple de trois, par exemple lors de l'utilisation d'unités de mesure telles que Kilo (10 ^ 3), Mega (10 ^ 6), Giga ( 10 ^ 9), et ainsi de suite.

Nous pouvons imposer ce type de notation en ajustant le nombre maximum de chiffres entiers (les caractères exprimés avec le # et à gauche du séparateur décimal) afin qu'il soit supérieur au nombre minimum (celui exprimé avec le 0) et supérieur à 1.

Cela oblige l'exposant à être un multiple du nombre maximal. Par conséquent, pour ce cas d'utilisation, nous voulons que le nombre maximal soit de trois:

assertThat(new DecimalFormat("##0.######E0")
  .format(d)).isEqualTo("1.23456789E6");
assertThat(new DecimalFormat("###.000000E0")
  .format(d)).isEqualTo("1.23456789E6");

6. Analyse

Voyons comment il est possible d'analyser unString en unNumber avec la méthode parse:

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);

Étant donné que la valeur renvoyée n’est pas déduite par la présence d’un séparateur décimal, nous pouvons utiliser les méthodes telles que.doubleValue(),.longValue() de l’objetNumber renvoyé pour appliquer une primitive spécifique en sortie.

On peut également obtenir unBigDecimal comme suit:

NumberFormat nf = new DecimalFormat(
  "",
  new DecimalFormatSymbols(Locale.ENGLISH));
((DecimalFormat) nf).setParseBigDecimal(true);

assertThat(nf.parse("1234567.89"))
  .isEqualTo(BigDecimal.valueOf(1234567.89));

7. Fil-Sécurité

DecimalFormat isn’t thread-safe, nous devons donc prêter une attention particulière lors du partage de la même instance entre les threads.

8. Conclusion

Nous avons vu les principaux usages de la classeDecimalFormat, ainsi que ses forces et ses faiblesses.

Comme toujours, le code source complet est disponibleover on Github.