Un guide pratique de DecimalFormat

1. Vue d'ensemble

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

Il s'agit d'une sous-classe de NumberFormat , qui permet de formater la représentation sous forme de chaîne de nombres décimaux à l' aide de modèles prédéfinis.

Il peut également être utilisé à l'inverse pour analyser les chaînes en nombres.

2. Comment ça marche?

Afin de mettre en forme un nombre, nous devons définir un modèle, qui est une séquence de caractères spéciaux potentiellement mélangés avec du texte.

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

  • 0 - imprime un chiffre s'il est fourni, 0 sinon
  • # - imprime un chiffre s'il est fourni, rien d'autre
  • . - indiquer où mettre le séparateur décimal
  • , - indiquer où placer le séparateur de regroupement

Lorsque le modèle est appliqué à un nombre, ses règles de mise en forme sont exécutées et le résultat est imprimé selon le DecimalFormatSymbol de notre JVM's Locale, sauf si un Locale spécifique est spécifié.

Les sorties des exemples suivants sont d'une machine virtuelle Java en cours d' exécution sur un Anglais Locale .

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 nous pouvons le voir, la partie entière n'est jamais ignorée, peu importe si le motif est 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 modèle est plus grand que le nombre, des zéros sont ajoutés, tandis que les hachages sont supprimés, à la fois dans l'entier et dans les parties décimales.

3.2. Arrondi

Si la partie décimale du modèle ne peut pas contenir toute la précision du nombre d'entrée, elle est arrondie.

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

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

Ici, la partie .89 a été arrondie à 1,00, puis le .00 a été abandonné et le 1 a été additionné au 7:

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

Le mode d'arrondi par défaut est HALF_EVEN, mais il peut être personnalisé via la méthode setRoundingMode .

3.3. Regroupement

Le séparateur de regroupement 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 à l'aide de la classe DecimalFormat , 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èle de regroupement multiple, il est nécessaire d'écrire notre propre code de manipulation de String , ou bien d'essayer le DecimalFormat d'Icu4J , qui le permet.

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

Il est possible de mélanger des littéraux String 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éraux de chaîne , par échappement:

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

4. Formatage localisé

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

Exécution de l'#, ###. ## motif sur une machine virtuelle Java avec un Italien Locale , par exemple, serait sortie 1.234.567,89.

Bien que cela puisse être une fonctionnalité i18n utile dans certains cas, dans d'autres, nous pouvons vouloir appliquer un format spécifique indépendant de la JVM.

Voici comment nous pouvons faire cela:

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 le Locale qui nous intéresse ne fait pas partie de ceux couverts par le constructeur DecimalFormatSymbols , nous pouvons le spécifier avec la méthode getInstance :

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

5. Notations scientifiques

La notation scientifique 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. E -Notation

Il est possible d'exprimer un nombre en notation scientifique en utilisant le caractère de motif E 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 de E00 et non de E0 .

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 force l'exposant à être un multiple du nombre maximum, donc pour ce cas d'utilisation, nous voulons que le nombre maximum soit 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 une chaîne en un nombre avec la méthode d'analyse:

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

Puisque la valeur retourné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' objet Number retourné pour appliquer une primitive spécifique en sortie.

Nous pouvons également obtenir un BigDecimal 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. Sécurité des fils

DecimalFormat n'est pas thread-safe , nous devons donc porter 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 classe DecimalFormat , ainsi que ses forces et ses faiblesses .

Comme toujours, le code source complet est disponible sur Github.