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!

Jeanne’s 1Z0-829 experience

I passed the 1Z0-829 today. The exam page still says “coming soon” and Scott didn’t see the 829 in the list of choices when he looked last night. I think the exam “wasn’t quite ready” and they pulled it back. (or accidentally released it too early). I guess I got to take it because I was registered already. I saw 5-6 errata on the exam which I have reported to Oracle.

COVID logistics

When I took the 819, I was asked to remove my mask twice – once for an ID check and once for a photo. This time, those steps were combined, so I only had to remove my mask once.

I hadn’t been to this test center before. It was walking distance and well set up. I was a good distance from any other test takers. The only problem I had was that I was right next to the radiator. It would have been hot even without a mask on. With a mask, I was sweating.

Other logistics

I was given a marker and since sheet to write on. I was not given an eraser. I asked for a second sheet and was given it though. (I write a lot.) The man in front of me asked for a second marker because it had run out once. They were flexible.

Some time ago, you could right click answers to cross them out of consideration. This feature is not yet back.

Time management

My first pass of the exam took 65 minutes. I then spent a good while examining and memorizing the questions that I believe to have errata. I didn’t get a chance to go through and sanity check my answers because I was tracking errata. If I wasn’t a cert book author, I’d have focused on review and gotten a higher score.

Getting the score

I got my score right when I submitted. (68%). I didn’t get a printout. But I didn’t need one since I had seen the score. My details were available on certview as soon as I got home as well.

You might notice the passing score is also 68%. Why so low you ask? A few reasons

  • Some of the errata resulted in having to guess at the answer. For example a question saying to pick two correct answers had three correct answers. So i had to guess what the exam creators meant. (I have reported all of these to Oracle, there were a bunch)
  • I didn’t check my answers because I was dealing with the above.
  • A few questions were things I didn’t expect to be in scope. (They will be covered in the book.) This is one of the disadvantages of being a cert book author – you have to take the exam without a study guide.)

I felt way more confident about this exam than I did the 819 though. I like the question distribution better and I didn’t have a COVID lockdown cloud hanging over me.

Question Distribution

When taking an exam, you have to agree not to share what was on it. So no details about what was covered. Sharing the distribution of questions by objective is fair game though!

Note that this is approximate because of relying on memory. And also because some questions spanned objectives

Objective# Questions
Handing date, time, text, numeric and boolean valuesLots
Controlling Program FlowLots
Utilizing Java Object-Oriented ApproachLots
Handling Exceptions3-5
Working with Arrays and Collections4-6
Working with Streams and Lambda expressions4-6
Package and deploy Java code and use the Java Platform Module System4
Manage concurrent code execution4
Use Java I/O API4
Access databases using JDBC2
Implement Localization2

An important disclaimer about randomness

With only 50 questions, randomness is a bigger factor. This means you could easily not see questions on a topic. Or get more than someone else on another topic. Be careful as you read the experiences of people who have taken the exam. Just because they didn’t get a question on X doesn’t mean that you won’t! So you don’t get to skip studying topics.

Oracle support EOL for Java LTS versions

The current end of life support dates are interesting. I extracted some dates from the official support table to make it easier to see this.

VersionGeneral AvailabilityPRemiereExtended
77/117/197/22
83/143/2210/30
119/189/239/26
179/219/269/29

First of all, extended support for Java 7 is less than a year away. You aren’t still on Java 7, right? 🙂

Second, premiere support for Java 11 ends later than Java 8. However, extended support for Java 8 ends way later. This is interesting. I guess Oracle recognizes it is harder to move off Java 8 than other versions?