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.

3
  • Do you want to handle M(ega) only, or also (G)iga, (T)era, etc? Commented Feb 9, 2009 at 19:22
  • It actually represents a security's volume, so it's M(illions) and potentially B(illions) but I'll be happy with just the M. Commented Feb 9, 2009 at 19:31
  • This related question has been getting much attention recently (due to a bounty). Commented Jun 11, 2015 at 1:05

8 Answers 8

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

For more information see the String.format javadocs.

Sign up to request clarification or add additional context in comments.

7 Comments

Just in case: use 1000000.0 instead.
Minor nitpick, looks like his code is using longs or ints so you can dispense with the floating point arithmetic:
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.
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.
Well, I have never used a NumberFormatter...so I don't know the easiest way to wrap it.
|
5

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

new DecimalFormat("#.00").format(value.movePointLeft(6)); 

Comments

5

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

Comments

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..."); } }; 

1 Comment

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
3

Why not simply?

DecimalFormat df = new DecimalFormat("0.00M"); System.out.println(df.format(n / 1000000)); 

1 Comment

The division needs to be encapsulated inside the formatter. Guess I'll update the question to more clearly explain the problem I'm having.
2

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() } } 

Comments

0

Take a look at ChoiseFormat.

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

1 Comment

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.
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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.