[step on the pit series] the long type is used to process the amount, and the scientific counting method causes the amount to be capitalized abnormally

Time:2021-6-9

1. Experience of stepping on the pit

Last week, a user reported that a sales order he created could not be opened, but the rest of the sales orders could be opened normally. At that time, he checked the error log of the production environment and found that such an exception was thrown:java.lang.NumberFormatException: For input string: "E"

I believe everyone is familiar with this exception. Obviously, it is thrown when converting a string to a number, such as the following:

However, after carefully looking at the user’s error reporting documents, we found no string like “e” (please forgive me for not thinking that it was caused by scientific counting method at the first time, ha ha). Finally, we inserted this data from the production environment into the development environment, and found that it was caused by converting the amount to capital. The key code for error reporting is as follows:

String totalAmountStr = String.valueOf(totalAmount / 100.0);

String amountCN = MoneyUtils.toChinese(totalAmountStr);

Where totalamount is a long variable. The reason why we divide it by 100.0 is that the amount stored in our database is calculated bybranchThe second line of code is mainly used to convert the amount to capital, such as 105000.50 to 1005000 yuan and fifty cents.

The total amount of the document that the user reported an error is 27 million, which is converted into a score of 27 milliontotalAmount / 100.0The output result is 2.7e7 instead of the expected 27000000, resulting in an exception:java.lang.NumberFormatException: For input string: "E"

The final solution is to convert the amount to BigDecimal, and the code is modified as follows:

String totalAmountStr = new BigDecimal(String.valueOf(totalAmount)).divide(new BigDecimal("100"),2, RoundingMode.HALF_UP).toString();
String amountCN = MoneyUtils.toChinese(totalAmountStr);

2. Cause analysis

In Java, when the integer part of floating point number (float, double) reaches 8 bits or more, it will be represented by scientific counting method, as follows:

double firstAmount = 2700000D;
double secondAmount = 27000000D;
double thirdAmount = 2700000.25D;
double fourthAmount = 27000000.25D;

System.out.println(firstAmount);
System.out.println(secondAmount);
System.out.println(thirdAmount);
System.out.println(fourthAmount);

Silently count down, the integer part of 8 words, are tens of millions of level, it is estimated that users encounter this problem is very proud, ha ha.

So use double to express the amount. When the amount meets the scientific counting method, it will display abnormally, or even cause some unexpected exceptions.

3. Solutions

If you do not want to display the amount by scientific counting method, there are two solutions:

  1. Using numberformat
  2. Using BigDecimal

3.1 scheme 1: use numberformat

The way to use numberformat is as follows:

NumberFormat numberFormat = NumberFormat.getInstance();
numberFormat.setGroupingUsed(false);

double secondAmount = 27000000D;
double fourthAmount = 27000000.25D;

System.out.println(numberFormat.format(secondAmount));
System.out.println(numberFormat.format(fourthAmount));

When willnumberFormat.setGroupingUsed(false);Note out or change tonumberFormat.setGroupingUsed(true);The output results change to:

3.2 scheme 2: use BigDecimal (recommended)

The way to use BigDecimal is as follows:

double secondAmount = 27000000D;
double fourthAmount = 27000000.25D;

System.out.println(new BigDecimal(String.valueOf(secondAmount)).setScale(2,RoundingMode.HALF_UP).toString());
System.out.println(new BigDecimal(String.valueOf(fourthAmount)).setScale(2,RoundingMode.HALF_UP).toString());

In contrast, I prefer BigDecimal’s solution.

For more usage of BigDecimal, see another blog I wrote:Java BigDecimal user’s Guide