14

Is there a way to use DecimalFormat (or some other standard formatter) to format numbers like this:

1,000,000 => 1.00M

1,234,567 => 1.23M

1,234,567,890 => 1234.57M

Basically dividing some number by 1 million, keeping 2 decimal places, and slapping an 'M' on the end. I've thought about creating a new subclass of NumberFormat but it looks trickier than I imagined.

I'm writing an API that has a format method that looks like this:

public String format(double value, Unit unit); // Unit is an enum

Internally, I'm mapping Unit objects to NumberFormatters. The implementation is something like this:

public String format(double value, Unit unit)
{
    NumberFormatter formatter = formatters.get(unit);
    return formatter.format(value);
}

Note that because of this, I can't expect the client to divide by 1 million, and I can't just use String.format() without wrapping it in a NumberFormatter.

Community
  • 1
  • 1
Tim Frey
  • 9,901
  • 9
  • 44
  • 60

8 Answers8

20
String.format("%.2fM", theNumber/ 1000000.0);

For more information see the String.format javadocs.

jjnguy
  • 136,852
  • 53
  • 295
  • 323
  • Just in case: use 1000000.0 instead. – Zach Scrivena Feb 09 '09 at 19:24
  • Minor nitpick, looks like his code is using longs or ints so you can dispense with the floating point arithmetic: – wds Feb 09 '09 at 19:30
  • If I leave off the floating point math, I would be left with a whole number, no decimal places at all. We need at least 2. – jjnguy Feb 09 '09 at 19:34
  • This works but I still need to wrap it with a NumberFormat object because of an additional constraint that I forgot to mention. Basically there is a MAP[Type => NumberFormatter] that this needs to play nicely with. – Tim Frey Feb 09 '09 at 19:35
  • Well, I have never used a NumberFormatter...so I don't know the easiest way to wrap it. – jjnguy Feb 09 '09 at 19:40
  • I decided that maybe trying to shoehorn this functionality into the existing class wasn't worth it, so I create a separate method and just used String.format as suggested here. Thanks! – Tim Frey Feb 09 '09 at 19:48
  • This will not be localized. Consider ICU's `CompactDecimalFormat` https://stackoverflow.com/a/55224088/9636 – Heath Borders Mar 18 '19 at 14:51
5

Note that if you have a BigDecimal, you can use the movePointLeft method:

new DecimalFormat("#.00").format(value.movePointLeft(6));
oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449
4

For someone looking out there to convert a given digit in human readable form.

public static String getHumanReadablePriceFromNumber(long number){

    if(number >= 1000000000){
        return String.format("%.2fB", number/ 1000000000.0);
    }

    if(number >= 1000000){
        return String.format("%.2fM", number/ 1000000.0);
    }

    if(number >= 100000){
        return String.format("%.2fL", number/ 100000.0);
    }

    if(number >=1000){
        return String.format("%.2fK", number/ 1000.0);
    }
    return String.valueOf(number);

}
3

Here's a subclass of NumberFormat that I whipped up. It looks like it does the job but I'm not entirely sure it's the best way:

private static final NumberFormat MILLIONS = new NumberFormat()
{
    private NumberFormat LOCAL_REAL = new DecimalFormat("#,##0.00M");

    public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos)
    {
        double millions = number / 1000000D;
        if(millions > 0.1) LOCAL_REAL.format(millions, toAppendTo, pos);

        return toAppendTo;
    }

    public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos)
    {
        return format((double) number, toAppendTo, pos);
    }

    public Number parse(String source, ParsePosition parsePosition)
    {
        throw new UnsupportedOperationException("Not implemented...");
    }
};
Tim Frey
  • 9,901
  • 9
  • 44
  • 60
  • I like the solution from Outlaw, especially as it can also create k/M/G "human readable" formatting without the need for the user of this api to do calculations. He always gets the shortest possible number. 999999 999.99k 999.99M 999.99G 999.99T – eckes Oct 24 '15 at 21:01
3

Why not simply?

DecimalFormat df = new DecimalFormat("0.00M");
System.out.println(df.format(n / 1000000));
toolkit
  • 49,809
  • 17
  • 109
  • 135
  • The division needs to be encapsulated inside the formatter. Guess I'll update the question to more clearly explain the problem I'm having. – Tim Frey Feb 09 '09 at 19:36
1

In Kotlin language, you can make extention function:

fun Long.formatToShortNumber(): String {
    return when {
        this >= 1000000000 -> String.format("%.2fB", this / 1000000000.0)
        this >= 1000000 -> String.format("%.2fM", this / 1000000.0)
        this >= 1000 -> String.format("%.2fK", this / 1000.0)
        else -> this.toString()
    }
}
Evstropov V
  • 472
  • 2
  • 10
0

Take a look at ChoiseFormat.

A more simplistic way would be to use a wrapper that auto divided by 1m for you.

sblundy
  • 60,628
  • 22
  • 121
  • 123
  • Took a look a the docs but I'm really not sure how that's going to help me here. Seems like ChoiceFormat basically contains a bunch of formats and somehow matches the input with one of these sub-formats. I think I want all input to be handled the same. – Tim Frey Feb 09 '09 at 19:14
0

For now, you should use ICU's CompactDecimalFormat, which will localize the formatting result for non-english locales. Other locales might not use a "Millions" suffix.

This functionality will be standard Java in JDK 12 with CompactNumberFormat.

Heath Borders
  • 30,998
  • 16
  • 147
  • 256