Java 12 - CompactNumberFormat Examples

Need to format posts, followers, or likes in a social-media-style, where 37211 becomes 37.2k?

@NatGeoTravel

@NatGeoTravel Twitter

In Java 12, there is a new class, CompactNumberFormat. This is a subclass of NumberFormat that formats a decimal number in a compact form. NumberFormat is the abstract base class for all number formats which provides the interface for formatting and parsing numbers. NumberFormat also provides methods for determining which locales have number formats, and what their names are.

An example of a SHORT compact form would be writing 10,000 as 10K, only requiring three instead of five characters. NumberFormat and java.text.spi.NumberFormatProvider have been extended to include a new method, getCompactNumberInstance().

There is also a related new enumeration, NumberFormat.Style, which has two values: LONG and SHORT.

CompactNumberFormatExample.java
import java.text.NumberFormat;
import java.util.Locale;

public class CompactNumberFormatExample {

    public static void main(String args[]) {

        NumberFormat nf = NumberFormat.getCompactNumberInstance(
                Locale.US, NumberFormat.Style.SHORT);
        
        System.out.println("NumberFormat.Style.SHORT:");
        System.out.println("Result: " + nf.format(10000));
        System.out.println("Result: " + nf.format(120300));
        System.out.println("Result: " + nf.format(2120000));
        System.out.println("Result: " + nf.format(1950000300));
        
        nf = NumberFormat.getCompactNumberInstance(
                Locale.US, NumberFormat.Style.LONG);
        
        System.out.println("\nNumberFormat.Style.LONG:");
        System.out.println("Result: " + nf.format(10000));
        System.out.println("Result: " + nf.format(120300));
        System.out.println("Result: " + nf.format(2120000));
        System.out.println("Result: " + nf.format(1950000300));
    }
}
                    

NumberFormat.Style.SHORT:
Result: 10K
Result: 120K
Result: 2M
Result: 2B

NumberFormat.Style.LONG:
Result: 10 thousand
Result: 120 thousand
Result: 2 million
Result: 2 billion

NumberFormat's Locale

We can specify desired locale for our compact number format. It is easiest to understand via code example:

CompactNumberFormatLocale.java
import java.text.NumberFormat;
import java.util.Locale;

public class CompactNumberFormatLocale {

    private static void printCompactNumberFormatLocale(long number) {
        NumberFormat nfDefault = NumberFormat.getCompactNumberInstance();
        NumberFormat nfUsLong = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.LONG);
        NumberFormat nfFrShort = NumberFormat.getCompactNumberInstance(Locale.FRANCE, NumberFormat.Style.SHORT);
        NumberFormat nfFrLong = NumberFormat.getCompactNumberInstance(Locale.FRANCE, NumberFormat.Style.LONG);
        NumberFormat nfChShort = NumberFormat.getCompactNumberInstance(Locale.CHINA, NumberFormat.Style.SHORT);
        NumberFormat nfChLong = NumberFormat.getCompactNumberInstance(Locale.CHINA, NumberFormat.Style.LONG);
        
        System.out.println("Compact Number Formatting for " + number + ":");
        System.out.println("Default:  " + nfDefault.format(number));
        System.out.println("US Long:  " + nfUsLong.format(number));
        System.out.println("FR Short: " + nfFrShort.format(number));
        System.out.println("FR Long:  " + nfFrLong.format(number));
        System.out.println("CH Short: " + nfChShort.format(number));
        System.out.println("CH Long:  " + nfChLong.format(number) + "\n");
    }

    public static void main(String[] arguments) {
        printCompactNumberFormatLocale(345600);
        printCompactNumberFormatLocale(23450000);
        printCompactNumberFormatLocale(1230000000);
    }
}
                    

Compact Number Formatting for 345600:
Default:  346K
US Long:  346 thousand
FR Short: 346 k
FR Long:  346 mille
CH Short: 35?
CH Long:  35?

Compact Number Formatting for 23450000:
Default:  23M
US Long:  23 million
FR Short: 23 M
FR Long:  23 million
CH Short: 2345?
CH Long:  2345?

Compact Number Formatting for 1230000000:
Default:  1B
US Long:  1 billion
FR Short: 1 Md
FR Long:  1 milliard
CH Short: 12?
CH Long:  12?

Following code is a snippet to get all locales for which the get*Instance methods of NumberFormat can return.

// available locales
Locale[] locales = NumberFormat.getAvailableLocales();
for (Locale locale : locales) {
    System.out.println(locale);
}
                    

NumberFormat's FractionDigits

CompactNumberFormatFractionDigits.java
import java.text.NumberFormat;
import java.util.Locale;

public class CompactNumberFormatFractionDigits {

    public static void main(String args[]) {

        NumberFormat nf = NumberFormat.getCompactNumberInstance(
                Locale.US, NumberFormat.Style.SHORT);
        System.out.println("Minimum: " + nf.getMinimumFractionDigits());
        System.out.println("Maximum: " + nf.getMaximumFractionDigits());
        
        System.out.println("\nResult: " + nf.format(123454));
        System.out.println("Result: " + nf.format(5156835));
        
        nf.setMaximumFractionDigits(1);        
        System.out.println("\nResult: " + nf.format(123454));
        System.out.println("Result: " + nf.format(5156835));
        
        nf.setMaximumFractionDigits(2);        
        System.out.println("\nResult: " + nf.format(123454));
        System.out.println("Result: " + nf.format(5156835));
        
        nf.setMinimumFractionDigits(2);   
        nf.setMaximumFractionDigits(2);        
        System.out.println("\nResult: " + nf.format(123454));
        System.out.println("Result: " + nf.format(5156835));
    }
}
                    

Minimum: 0
Maximum: 0

Result: 123K
Result: 5M

Result: 123.5K
Result: 5.2M

Result: 123.45K
Result: 5.16M

Result: 123.45K
Result: 5.16M

The ability to use fraction digits leads to the ability to control compact number formatted output that is likely to be more aesthetically pleasing in needed cases.

NumberFormat's Rounding

By default RoundingMode.HALF_EVEN is used. We can set rounding mode by calling setRoundingMode(RoundingMode) method.

The rounding related to fraction digits

CompactNumberFormatRounding.java
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Locale;

public class CompactNumberFormatRounding {

    public static void main(String args[]) {

        NumberFormat nf = NumberFormat.getCompactNumberInstance(
                Locale.US, NumberFormat.Style.SHORT);
        
        System.out.println("\nResult: " + nf.format(123454));
        System.out.println("Result: " + nf.format(5156835));
        
        nf.setMinimumFractionDigits(2);   
        nf.setMaximumFractionDigits(2);
        System.out.println("\nResult: " + nf.format(123454));
        System.out.println("Result: " + nf.format(5156835));
        
        nf.setRoundingMode(RoundingMode.UP);
        System.out.println("\nResult: " + nf.format(123454));
        System.out.println("Result: " + nf.format(5156835));
        
        nf.setMinimumFractionDigits(1); 
        nf.setMaximumFractionDigits(1);        
        System.out.println("\nResult: " + nf.format(123454));
        System.out.println("Result: " + nf.format(5156835));
        
        nf.setRoundingMode(RoundingMode.DOWN);
        System.out.println("\nResult: " + nf.format(123454));
        System.out.println("Result: " + nf.format(5156835));
    }
}
                    

Result: 123K
Result: 5M

Result: 123.45K
Result: 5.16M

Result: 123.46K
Result: 5.16M

Result: 123.5K
Result: 5.2M

Result: 123.4K
Result: 5.1M

NumberFormat's Parse

To return a number from a text, we use parse method.

CompactNumberFormatParse.java
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

public class CompactNumberFormatParse {

    public static void main(String[] args) throws ParseException {
        NumberFormat nf = NumberFormat.getCompactNumberInstance(
                Locale.US, NumberFormat.Style.SHORT);
        System.out.println("US/SHORT parsing:");
        System.out.println(nf.parse("1K"));
        System.out.println(nf.parse("1M"));
        System.out.println(nf.parse("1B"));

        nf = NumberFormat.getCompactNumberInstance(
                Locale.US, NumberFormat.Style.LONG);
        System.out.println("\nUS/LONG parsing:");
        System.out.println(nf.parse("1 thousand"));
        System.out.println(nf.parse("1 million"));
        System.out.println(nf.parse("1 billion"));
    }
}
                    

US/SHORT parsing:
1000
1000000
1000000000

US/LONG parsing:
1000
1000000
1000000000

The Locale as always will determine the parsing result (as in format result). Here the example if we use FR locale:

CompactNumberFormatParseFr.java
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

public class CompactNumberFormatParseFr {

    public static void main(String[] args) throws ParseException {
        NumberFormat nf = NumberFormat.getCompactNumberInstance(
                Locale.FRANCE, NumberFormat.Style.SHORT);
        System.out.println("FR/SHORT parsing:");
        System.out.println("1K: " + nf.parse("1K"));
        System.out.println("1 k: " + nf.parse("1 k"));
        System.out.println("1M: " + nf.parse("1M"));
        System.out.println("1 M: " + nf.parse("1 M"));
        System.out.println("1B: " + nf.parse("1B"));
        System.out.println("1 Md:" + nf.parse("1 Md"));

        nf = NumberFormat.getCompactNumberInstance(
                Locale.FRANCE, NumberFormat.Style.LONG);
        System.out.println("\nFR/LONG parsing:");
        System.out.println("1 thousand: " + nf.parse("1 thousand"));
        System.out.println("1 mille: " + nf.parse("1 mille"));
        System.out.println("1 million: " + nf.parse("1 million"));
        System.out.println("1 billion: " + nf.parse("1 billion"));
        System.out.println("1 milliard: " + nf.parse("1 milliard"));
    }
}
                    

FR/SHORT parsing:
1K: 1
1 k: 1000
1M: 1
1 M: 1000000
1B: 1
1 Md:1000000000

FR/LONG parsing:
1 thousand: 1
1 mille: 1000
1 million: 1000000
1 billion: 1000000000000
1 milliard: 1000000000

Note: The space character used in FR SHORT format is NO-BREAK SPACE (U+00A0) or character 160, not SPACE (U+0020) or character 32.

Parse with Grouping

By default 'grouping used' is set to 'false', this made default parsing behavior does not allow a grouping separator until 'grouping used' is set to true. To change 'grouping used' value, we can use method setGroupingUsed(boolean).

CompactNumberFormatParseGrouping.java
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

public class CompactNumberFormatParseGrouping {

    public static void main(String[] args) throws ParseException {
        NumberFormat nf = NumberFormat.getCompactNumberInstance(
                Locale.US, NumberFormat.Style.SHORT);
        System.out.println("Without Grouping:");
        System.out.println(nf.parse("1,000K"));
        System.out.println(nf.parse("1,000M"));
        System.out.println(nf.parse("1,000B"));
        
        nf.setGroupingUsed(true);
        System.out.println("\nWith Grouping:");
        System.out.println(nf.parse("1,000K"));
        System.out.println(nf.parse("1,000M"));
        System.out.println(nf.parse("1,000B"));
    }
}
                    

Without Grouping:
1
1
1

With Grouping:
1000000
1000000000
1000000000000