Java CompactNumberFormat Bug or Feature?

Java 12 introduced a new CompactNumberFormat class, which anyone studying for the new Java 17 1Z0-829 OCP certification should know. It’s really cool utility feature, helping to shorten lengthy number values into shorter forms for common usage. It supports a Style setting, SHORT (1M) vs LONG (1 million), as well as rounding, and many other features. Generally speaking, it rounds the value to the first human-readable 3-digit tuple, formats it, and then adds a label depending on style/locale.

Let’s take a look at an example:

var shortCNF = NumberFormat.getCompactNumberInstance(Locale.US,
   Style.SHORT);
var longCNF = NumberFormat.getCompactNumberInstance(Locale.US,
   Style.LONG);
System.out.print(shortCNF.format(15_300));          // 15K
System.out.print(longCNF.format(15_300));           // 15 thousand
System.out.print(shortCNF.format(124_000_200));     // 124M
System.out.print(longCNF.format(124_000_200));      // 124 million
System.out.print(shortCNF.format(4_834_000_000.0)); // 5B
System.out.print(longCNF.format(4_834_000_000.0));  // 5 billion

Useful stuff, right? Notice the last two examples rounded the value up to 5 bllion? Rounding (which can be disabled) is enabled by default. Well, while writing some really tricky practice exam questions for our upcoming Java OCP 17 Practice Test Book, I discovered something rather odd:

var shortCNF = NumberFormat.getCompactNumberInstance(Locale.US,
   Style.SHORT);
var longCNF = NumberFormat.getCompactNumberInstance(Locale.US,
   Style.LONG);
System.out.print(shortCNF.format(999_999));   // 1000K (this is weird)
System.out.print(longCNF.format(999_999));    // 1000 thousand (this is weird)
System.out.print(shortCNF.format(1_000_000)); // 1M
System.out.print(longCNF.format(1_000_000));  // 1 million
System.out.print(shortCNF.format(1_999_999)); // 2M
System.out.print(longCNF.format(1_999_999));  // 2 million

Notice the issue? If the CompactNumberFormat rounds up and enters a new range (thousand to million, million to billion, etc), it doesn’t adjust the labels or values. The first two sets of values should print 1M and 1 million, but the rounded value prints 1000K and 1000 thousand instead. While I used Locale.US for reproducibility, this isn’t required. It appears when you use other locales, and other ranges. For instance, 999_999_999 formats as 1000M, instead of 1B. I validated on Oracle’s latest release of Java 17.0.2.

So.. is this a bug or a feature? It partially depends how you read the unicode spec the Java feature was based on. The spec covers formatting rules and order of operation, but it doesn’t provide as much insight on how rounding is supposed to be handled in this particular situation.

I believe this is a bug because:

  • No one would ever expect (or want) to see one million written as 1000 thousand or 1000K. If you saw that on a website or mobile app, you’d likely report it as a bug. (If you’re a developer using this feature, you would probably then be told to stop using this library altogether!)
  • If it is working as designed, then the spec has a problem. The only work-around for someone who wants to use CompactNumberFormat without encountering this issue is to either disable rounding, or round the value ahead of time. In either situation, the utility of using the CompactNumberFormat feature drops precipitously.

To me, it’s a bug…. or a feature that renders CompactNumberFormat not suitable for practical use. With that in mind, I opened a bug ticket JDK-8281317 with Oracle to address the issue. I will update this page when I get a response!

Side note: On February 6, I created a Twitter poll and interestingly enough the correct answer of 1000K was the least selected option! Certainly, not an intuitive implementation!

why to turn down a paid job

More money is always better, right?  Not really.  Granted if you don’t have enough money to buy food and other necessities, this doesn’t apply to you.  However if you already have a full time job and enough money to live comfortably, is more money better?

Liking your full time job

One time in the Job Discussion forum of JavaRanch, someone asked about which of two completely disparate things paid better.  In a discussion about which he liked better, I asked a hypothetical question.  The gist of it was – if you got paid a lot of money to get poked with a hot/sharp stick 40 hours a week for the next 10-20 years, would you do it?”  While someone obligingly answered that it depends on how much you get paid, it shows the point.  Making you think about enjoying the large percentage of time you spend at work.

Similarly, a few participants in that forum have expressed a statement that they would rather stay technical even if becoming a manager paid more.  I certainly understand this sentiment.  It shows enjoying a job matters.

What about volunteering?

Some technical people do volunteer work on the side because they enjoy it.  This is a marked contrast to “I’m on a computer all day at work – why would I want to touch one at home.”  On the surface, it would seem like if you enjoy something enough to do it voluntarily, getting paid would be icing on the cake.  I’ve turned down a couple offers to do something on the side for money.  My reason is that I have a full time job and if I get paid to do X, it will be stressful.  Whereas while I’m a volunteer I don’t feel bad if I have to stop for a little while – as long as someone else is covering.

Then I read Ilja Preuss’ tweet about the article “How Rewards can Backfire and Reduce Motivation.”  The article points out rewards remind us of obligations and dull/tedious/painful tasks.  Very interesting and it really resonates with me and why I do things as a volunteer.

Assuming you can pay the bills, fun is a factor!