Core Java

Java Numeric Formatting: DecimalFormat

In the post Java Numeric Formatting, I described and demonstrated some of the useful instances provided by NumberFormat static methods such as NumberFormat.getNumberInstance(Locale), NumberFormat.getPercentInstance(Locale), NumberFormat.getCurrencyInstance(Locale), and NumberFormat.getIntegerInstance(Locale). It turns out that all of these instances of abstract NumberFormat are actually instances of DecimalFormat, which extends NumberFormat.

The next code listing and the associated output demonstrate that all instances returned by NumberFormat‘s “getInstance” methods are actually DecimalFormat instances. What differentiates these instances of the same DecimalFormat class is the settings of their attributes such as minimum and maximum integer digits (digits to the left of the decimal point) and minimum and maximum number of fraction digits (digits to the right of the decimal point). They all share the same rounding mode and currency settings.

Instances Provided by NumberFormat.getInstance() Are DecimalFormat Instances

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
 * Write characteristics of provided Currency object to
 * standard output.
 *
 * @param currency Instance of Currency whose attributes
 *    are to be written to standard output.
 */
public void printCurrencyCharacteristics(final Currency currency)
{
   out.print("\tCurrency: " + currency.getCurrencyCode()
      + "(ISO 4217 Code: " + currency.getNumericCode() + "), ");
   out.println(currency.getSymbol() + ", (" + currency.getDisplayName() + ")");
}
 
/**
 * Writes characteristics of provided NumberFormat instance
 * to standard output under a heading that includes the provided
 * description.
 *
 * @param numberFormat Instance of NumberFormat whose key
 *    characteristics are to be written to standard output.
 * @param description Description to be included in standard
 *    output.
 */
public void printNumberFormatCharacteristics(
   final NumberFormat numberFormat, final String description)
{
   out.println(description + ": " + numberFormat.getClass().getCanonicalName());
   out.println("\tRounding Mode:           " + numberFormat.getRoundingMode());
   out.println("\tMinimum Fraction Digits: " + numberFormat.getMinimumFractionDigits());
   out.println("\tMaximum Fraction Digits: " + numberFormat.getMaximumFractionDigits());
   out.println("\tMinimum Integer Digits:  " + numberFormat.getMinimumIntegerDigits());
   out.println("\tMaximum Integer Digits:  " + numberFormat.getMaximumIntegerDigits());
   printCurrencyCharacteristics(numberFormat.getCurrency());
   if (numberFormat instanceof DecimalFormat)
   {
      final DecimalFormat decimalFormat = (DecimalFormat) numberFormat;
      out.println("\tPattern: " + decimalFormat.toPattern());
   }
}
 
/**
 * Display key characteristics of the "standard"
 * NumberFormat/DecimalFormat instances returned by the static
 * NumberFormat methods getIntegerInstance(), getCurrencyInstance(),
 * getPercentInstance(), and getNumberInstance().
 */
public void demonstrateDecimalFormatInstancesFromStaticNumberFormatMethods()
{
   final NumberFormat integerInstance = NumberFormat.getIntegerInstance();
   printNumberFormatCharacteristics(integerInstance, "IntegerInstance");
   final NumberFormat currencyInstance = NumberFormat.getCurrencyInstance();
   printNumberFormatCharacteristics(currencyInstance, "CurrencyInstance");
   final NumberFormat percentInstance = NumberFormat.getPercentInstance();
   printNumberFormatCharacteristics(percentInstance, "PercentInstance");
   final NumberFormat numberInstance = NumberFormat.getNumberInstance();
   printNumberFormatCharacteristics(numberInstance, "NumberInstance");
}

numberFormatStaticProvidedInstancesAreDecimalFormatInstances

Although my previous post and this post so far have demonstrated obtaining instances of DecimalFormat via static NumberFormat access methods, DecimalFormat also has three overloaded constructors DecimalFormat(), DecimalFormat(String), and DecimalFormat(String, DecimalFormatSymbols). Note, however, that there is a warning in DecimalFormat’s Javadoc documentation stating, “In general, do not call the DecimalFormat constructors directly, since the NumberFormat factory methods may return subclasses other than DecimalFormat.” My next few examples do instantiate DecimalFormat instances with their direct constructors despite that Javadoc warning because there is no harm in this case of doing so.

Instances of DecimalFormat support a great degree of control over the presentation formatting of decimal numbers. The following code runs the standard set of numbers used in the earlier example against a variety of different custom patterns. The screen snapshot after the code listing shows how these numbers are rendered when these patterns are applied.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
 * Apply provided pattern to DecimalFormat instance and write
 * output of application of that DecimalFormat instance to
 * standard output along with the provided description.
 *
 * @param pattern Pattern to be applied to DecimalFormat instance.
 * @param description Description of pattern being applied.
 */
private void applyPatternToStandardSample(
   final String pattern, final String description)
{
   final DecimalFormat decimalFormat = new DecimalFormat(pattern);
   printHeader(description + " - Applying Pattern '" + pattern + "'");
   for (final double theDouble : ourStandardSample)
   {
      out.println(
         theDouble + ": " + decimalFormat.format(theDouble));
   }
}
 
/**
 * Demonstrate various String-based patters applied to
 * instances of DecimalFormat.
 */
public void demonstrateDecimalFormatPatternStringConstructor()
{
   final String sixFixedDigitsPattern = "000000";
   applyPatternToStandardSample(sixFixedDigitsPattern, "Six Fixed Digits");
   final String sixDigitsPattern = "###000";
   applyPatternToStandardSample(sixDigitsPattern, "Six Digits Leading Zeros Not Displayed");
   final String percentagePattern = "";
   applyPatternToStandardSample(percentagePattern, "Percentage");
   final String millePattern = "\u203000";
   applyPatternToStandardSample(millePattern, "Mille");
   final String currencyPattern = "\u00A4";
   applyPatternToStandardSample(currencyPattern, "Currency");
   final String internationalCurrencyPattern = "\u00A4";
   applyPatternToStandardSample(internationalCurrencyPattern, "Double Currency");
   final String scientificNotationPattern = "0.###E0";
   applyPatternToStandardSample(scientificNotationPattern, "Scientific Notation");
}
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
==================================================================
= Six Fixed Digits - Applying Pattern '000000'
==================================================================
NaN: �
0.25: 000000
0.4: 000000
0.567: 000001
1.0: 000001
10.0: 000010
100.0: 000100
1000.0: 001000
10000.0: 010000
100000.0: 100000
1000000.0: 1000000
1.0E7: 10000000
Infinity: ∞
==================================================================
= Six Digits Leading Zeros Not Displayed - Applying Pattern '###000'
==================================================================
NaN: �
0.25: 000
0.4: 000
0.567: 001
1.0: 001
10.0: 010
100.0: 100
1000.0: 1000
10000.0: 10000
100000.0: 100000
1000000.0: 1000000
1.0E7: 10000000
Infinity: ∞
==================================================================
= Percentage - Applying Pattern ''
==================================================================
NaN: �
0.25: %25
0.4: %40
0.567: %57
1.0: %100
10.0: %1000
100.0: %10000
1000.0: %100000
10000.0: %1000000
100000.0: %10000000
1000000.0: %100000000
1.0E7: %1000000000
Infinity: %∞
==================================================================
= Mille - Applying Pattern '‰00'
==================================================================
NaN: �
0.25: ‰250
0.4: ‰400
0.567: ‰567
1.0: ‰1000
10.0: ‰10000
100.0: ‰100000
1000.0: ‰1000000
10000.0: ‰10000000
100000.0: ‰100000000
1000000.0: ‰1000000000
1.0E7: ‰10000000000
Infinity: ‰∞
==================================================================
= Currency - Applying Pattern '¤'
==================================================================
NaN: �
0.25: $0
0.4: $0
0.567: $1
1.0: $1
10.0: $10
100.0: $100
1000.0: $1000
10000.0: $10000
100000.0: $100000
1000000.0: $1000000
1.0E7: $10000000
Infinity: $∞
==================================================================
= Double Currency - Applying Pattern '¤'
==================================================================
NaN: �
0.25: $0
0.4: $0
0.567: $1
1.0: $1
10.0: $10
100.0: $100
1000.0: $1000
10000.0: $10000
100000.0: $100000
1000000.0: $1000000
1.0E7: $10000000
Infinity: $∞
==================================================================
= Scientific Notation - Applying Pattern '0.###E0'
==================================================================
NaN: �
0.25: 2.5E-1
0.4: 4E-1
0.567: 5.67E-1
1.0: 1E0
10.0: 1E1
100.0: 1E2
1000.0: 1E3
10000.0: 1E4
100000.0: 1E5
1000000.0: 1E6
1.0E7: 1E7
Infinity: ∞

For my last two examples of applying DecimalFormat, I will acquire the instance of DecimalFormat via the preferred approach of using NumberFormat.getInstance(Locale). The first code listing demonstrates different locales applied to the same double and then the output format from each.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
 * Provides an instance of DecimalFormat based on the provided instance
 * of Locale.
 *
 * @param locale Locale to be associated with provided instance of
 *    DecimalFormat.
 * @return Instance of DecimalFormat associated with provided Locale.
 * @throws ClassCastException Thrown if the object provided to me
 *    by NumberFormat.getCurrencyInstance(Locale) is NOT an instance
 *    of class {@link java.text.DecimalFormat}.
 */
private DecimalFormat getDecimalFormatWithSpecifiedLocale(final Locale locale)
{
   final NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
   if (!(numberFormat instanceof DecimalFormat))
   {
      throw new ClassCastException(
         "NumberFormat.getCurrencyInstance(Locale) returned an object of type "
            + numberFormat.getClass().getCanonicalName() + " instead of DecimalFormat.");
   }
   return (DecimalFormat) numberFormat;
}
 
/**
 * Demonstrate formatting of double with various Locales.
 */
public void demonstrateDifferentLocalesCurrencies()
{
   final double monetaryAmount = 14.99;
   out.println("Locale-specific currency representations of " + monetaryAmount + ":");
   out.println("\tLocale.US:            "
      + getDecimalFormatWithSpecifiedLocale(Locale.US).format(monetaryAmount));
   out.println("\tLocale.UK:            "
      + getDecimalFormatWithSpecifiedLocale(Locale.UK).format(monetaryAmount));
   out.println("\tLocale.ENGLISH:       "
      + getDecimalFormatWithSpecifiedLocale(Locale.ENGLISH).format(monetaryAmount));
   out.println("\tLocale.JAPAN:         "
      + getDecimalFormatWithSpecifiedLocale(Locale.JAPAN).format(monetaryAmount));
   out.println("\tLocale.GERMANY:       "
      + getDecimalFormatWithSpecifiedLocale(Locale.GERMANY).format(monetaryAmount));
   out.println("\tLocale.CANADA:        "
      + getDecimalFormatWithSpecifiedLocale(Locale.CANADA).format(monetaryAmount));
   out.println("\tLocale.CANADA_FRENCH: "
      + getDecimalFormatWithSpecifiedLocale(Locale.CANADA_FRENCH).format(monetaryAmount));
   out.println("\tLocale.ITALY:         "
      + getDecimalFormatWithSpecifiedLocale(Locale.ITALY).format(monetaryAmount));
}
1
2
3
4
5
6
7
8
9
Locale-specific currency representations of 14.99:
 Locale.US:            $14.99
 Locale.UK:            £14.99
 Locale.ENGLISH:       ¤14.99
 Locale.JAPAN:         ¥15
 Locale.GERMANY:       14,99 €
 Locale.CANADA:        $14.99
 Locale.CANADA_FRENCH: 14,99 $
 Locale.ITALY:         € 14,99

My DecimalFormat examples so far have focused on formatting numbers for presentation. This final example goes the other direction and parses a value from the string representation.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
/**
 * Demonstrate parsing.
 */
public void demonstrateParsing()
{
   final NumberFormat numberFormat = NumberFormat.getCurrencyInstance(Locale.US);
   final double value = 23.23;
   final String currencyRepresentation = numberFormat.format(value);
   out.println("Currency representation of " + value + " is " + currencyRepresentation);
   try
   {
      final Number parsedValue = numberFormat.parse(currencyRepresentation);
      out.println("Parsed value of currency representation " + currencyRepresentation + " is " + parsedValue);
   }
   catch (ParseException parseException)
   {
      out.println("Exception parsing " + currencyRepresentation + parseException);
   }
}
1
2
Currency representation of 23.23 is $23.23
Parsed value of currency representation $23.23 is 23.23

The last example shown did not actually need to access the concrete DecimalNumber methods and was able to solely use the NumberFormat-advertised methods. The example formats a currency representation with NumberFormat.format(double) and then parses that provided currency representation to return to the original value with NumberFormat.parse(String).

NumberFormat, and more specifically DoubleFormat, “format and parse numbers for any locale.”

Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button