Practical tips 18: improper use of BigDecimal division leads to accuracy problems

When using BigDecimal's division method, I encountered a ghost animal problem. Originally, I thought the accuracy was calculated, and the result returned 0. Of course, it was finally found that the posture was wrong. Therefore, record it to avoid repeating the mistakes later

1. Problem throw

When using BigDecimal to do high-precision division, I accidentally encountered a small problem, as shown below

@Test
public void testBigDecimal() {
    BigDecimal origin = new BigDecimal(541253);
    BigDecimal now = new BigDecimal(12389431);

    BigDecimal val = origin.divide(now, RoundingMode.HALF_UP);
    System.out.println(val);

    origin = new BigDecimal(541253);
    now = new BigDecimal(12389431.3);
    val = origin.divide(now, RoundingMode.HALF_UP);
    System.out.println(val);

    origin = new BigDecimal(541253.4);
    now = new BigDecimal(12389431);
    val = origin.divide(now, RoundingMode.HALF_UP);
    System.out.println(val);
}
Copy code

What is the output above?

0
0
0.043686703610520937021487456961257
 Copy code

Why are the first two 0? It's understandable if 541253 / 12389431 = 0 directly, but BigDecimal is not a high-precision calculation. It's reasonable that there should be no such problem of division

We know that when BigDecimal is triggered, you can specify the parameter to keep decimal. If you add this, will it be different?

BigDecimal origin = new BigDecimal(541253);
BigDecimal now = new BigDecimal(12389431);

BigDecimal val = origin.divide(now, 5, RoundingMode.HALF_UP);
System.out.println(val);
Copy code

The output result is:

0.04369
 Copy code

Therefore, there is no problem after the decimal point is specified. So let's make a bold guess. In the above case s, when the scale value is not specified, the default value is different, resulting in different accuracy of the final result?

After a simple in-depth analysis of the source code, the execution method is origin.divide(now, RoundingMode.HALF_UP);, Therefore, the scale parameter is aimed at the origin object, and this object can only analyze its structure, because there is no other place to use it

2. Source location

1. Shaping parameter transmission structure

Analyze the following line and go directly to the source code

BigDecimal origin = new BigDecimal(541253);
Copy code

Obviously, the int parameter passing structure. Go in and have a simple look

// java.math.BigDecimal#BigDecimal(int)
public BigDecimal(int val) {
    this.intCompact = val;
    this.scale = 0;
    this.intVal = null;
}

public BigDecimal(long val) {
    this.intCompact = val;
    this.intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
    this.scale = 0;
}
Copy code

so, it is clear that the default scale is 0, that is, when the origin is a positive number, the division is performed by it. It is unrealistic to specify the scale parameter, and the final return is no decimal. Similarly, take a look at the parameter transmission method of long, and BigInteger is the same

2. Floating point parameter transfer

The next step is to confirm the default value of floating-point scale. This structure is a little more complex than the previous one. The source code is not pasted. It is too long and you can't understand what you have done. You can directly enter the debug mode in a more obscene way and execute it step by step

@Test
public void testBigDecimal() {
    BigDecimal origin = new BigDecimal(541253.0);
    BigDecimal now = new BigDecimal(12389431.1);
    BigDecimal tmp = new BigDecimal(0.0);
}
Copy code

According to the result of debug, the first scale is 0; The second scale is 29 and the third scale is 0

3. String parameter transfer

It is still a large string of logic. Try the same way with one-step debug

@Test
public void testBigDecimal() {
    BigDecimal origin = new BigDecimal("541253.0");
    BigDecimal now = new BigDecimal("12389431.1");
    BigDecimal t = new BigDecimal("0.0");
}
Copy code

The scale of the above three is 1

4. Summary

  • For BigDecimal, it is best to specify its scale parameter when performing division operation, otherwise there may be pits
  • For the principle of BigDecimal's scale initialization, we need to take an in-depth look at how BigDecimal is implemented

Finally, a graph of multiplication is pasted as a conclusion

Series blog posts:

2. Other

1. A gray Blog: liuyueyi.github.io/hexblog

If you believe in a book, it's better to have no book. It's just the words of a family. Due to your limited personal ability, it's inevitable to have omissions and mistakes. If you find a bug or have better suggestions, you're welcome to criticize and correct and appreciate it

Posted on Tue, 02 Nov 2021 00:42:06 -0400 by skkeeper