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
/** * 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"); }
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.
/** * 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"); }
================================================================== = 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.
/** * 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)); }
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.
/** * 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); } }
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.”
Reference: | Java Numeric Formatting: DecimalFormat from our JCG partner Dustin Marx at the Inspired by Actual Events blog. |