JDK-8281317 : CompactNumberFormat displays 4-digit values when rounding to a new range
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.text
  • Affected Version: 17,18,19
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2022-02-07
  • Updated: 2022-02-23
  • Resolved: 2022-02-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 19
19 masterFixed
Description
ADDITIONAL SYSTEM INFORMATION :
Tested with Oracle JDK 17.0.2.  Operating system not relevant.

A DESCRIPTION OF THE PROBLEM :
The CompactNumberFormat class was introduced to make displaying large values in a compact manner consistent and easy across different implementations. For example, the value "5,213,222" being displayed as "5M".  By default, the CompactNumberFormat supports rounding.  For instance, "523,999" is formatted to 524K.

There appears to be an issue, though, when rounding causes the overall term to increase, say from thousand to million, or million to billion.  For example, with rounding enabled, the values 999,999 and 1,000,000 should both format to "1M" using a short STYLE formatter. Instead, it prints as 1000K and 1M, respectively.  Anyone displaying data coming from a CompactNumberFormat would see 1000K and quickly realize this is a display error.  Note that this problem happens regardless of if the style SHORT or LONG is selected.  In the case of LONG, the format would be "1000 thousand", which is even worse to see in a real application.

This behavior appears to be a bug related to rounding. If not a bug, then the CompactNumberFormat is not suitable for practical use.  If a business displayed a monetary value as $1000K or "1000 thousand", instead of $1M,  it would be very unprofessional.  In other words, if this isn't a bug and the spec is correct, then the existence of this issue defeats the purpose of using CompactNumberFormat in the first place.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Sample code to reproduce the issue:

var nf1 = NumberFormat.getCompactNumberInstance(Locale.US,Style.SHORT);
System.out.println(nf1.format(999_999));
System.out.println(nf1.format(1_000_000));

System.out.println(nf1.format(1_999_999));
System.out.println(nf1.format(2_000_000));

var nf2 = NumberFormat.getCompactNumberInstance(Locale.US,Style.LONG);
System.out.println(nf2.format(999_999_999));
System.out.println(nf2.format(1_000_000_000));

System.out.println(nf2.format(1_999_999_999));
System.out.println(nf2.format(2_000_000_000));

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
1M
1M
2M
2M
1 billion
1 billion
2 billion
2 billion
ACTUAL -
1000K
1M
2M
2M
1000 million
1 billion
2 billion
2 billion

---------- BEGIN SOURCE ----------
import java.text.NumberFormat;
import java.text.NumberFormat.Style;
import java.util.Locale;

public class SampleCode {
	public static void main(String[] arg) {
		var nf1 = NumberFormat.getCompactNumberInstance(Locale.US,Style.SHORT);
		System.out.println(nf1.format(999_999));
		System.out.println(nf1.format(1_000_000));
		
		System.out.println(nf1.format(1_999_999));
		System.out.println(nf1.format(2_000_000));
		
		var nf2 = NumberFormat.getCompactNumberInstance(Locale.US,Style.LONG);
		System.out.println(nf2.format(999_999_999));
		System.out.println(nf2.format(1_000_000_000));

		System.out.println(nf2.format(1_999_999_999));
		System.out.println(nf2.format(2_000_000_000));
	}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
The only work-around would be to disable rounding, or round the value before sending it to a CompactNumberFormat.  Or don't use CompactNumberFormat at all because it is not useful.

FREQUENCY : always



Comments
Changeset: 12927765 Author: Naoto Sato <naoto@openjdk.org> Date: 2022-02-17 19:03:08 +0000 URL: https://git.openjdk.java.net/jdk/commit/129277653e51e9b1387ecee279a6ccee9199c8ff
17-02-2022

A pull request was submitted for review. URL: https://git.openjdk.java.net/jdk/pull/7412 Date: 2022-02-09 22:37:45 +0000
14-02-2022

The observations on Windows 10: JDK 11: The reproducer is not applicable. JDK 17: Failed, 999_999 returns 1000K and 999_999_999 returns 1000 million JDK 18ea+29: Failed. JDK 19ea+3: Failed.
07-02-2022