BigDecimal operation double, float precision loss problem

One, the problem

Recently, when using BigDecimal for numerical addition and subtraction, I stepped on a small pit: BigDecimal lost precision when operating double and float values.
for example:

    public static void main(String[] args) {
        float d1 = 1.2f;
        float d2 = 2.1f;
        BigDecimal b1 = new BigDecimal(d1);
        BigDecimal b2 = new BigDecimal(d2);
        System.out.println(b1.add(b2));
    }

The ideal output result is 3.3, but the actual output result is 3.2999999523162841796875. If this number is multiplied by 1000000000, this may cause the company to lose thousands of dollars, only due to the loss of precision.

We know that Java simple types cannot perform precise floating-point operations, and BigDecimal is used to perform precise floating-point operations on numbers with more than 16 digits. So why is there a loss of precision in the example just given?

The following is the explanation of BigDecimal's construction method document:

public BigDecimal(double val)
converts double to BigDecimal, which is the accurate decimal representation of the binary floating point value of double. The scale of the returned BigDecimal is the minimum value such that (10scale × val) is an integer.

That is: the
BigDecimal created by new BigDecimal(0.1) is exactly equal to 0.1 (unscaled value 1, and its scale is 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be accurately represented as a double (or in this case, it cannot be represented as any finite-length binary decimal). In this way, the value passed into the constructor will not be exactly equal to 0.1 (although on the surface it is equal to this value).

From the documentation of BigDecima, it can be seen that when using BigDecimal's constructor to encapsulate Double and Float values, there will be errors between the actually created value and the value we expect, so there will be a loss of precision when performing operations.

2. How to avoid

When using, there are two methods:

  • Use new BigDecimal(String);
  • 使用BigDecimal.valuOf(Double);

1、new BigDecimal(String):

    public static void main(String[] args) {
        double d1 = 1.2;
        double d2 = 2.1;
        BigDecimal b1 = new BigDecimal(d1.toString());
        BigDecimal b2 = new BigDecimal(d2.toString());
        System.out.println(b1.add(b2));
    }

The output result is: 3.3

2. The use of BigDecimal.valuOf (double):

    public static void main(String[] args) {
        double d1 = 1.2;
        double d2 = 2.1;
        BigDecimal b1 = BigDecimal.valueOf(d1);
        BigDecimal b2 = BigDecimal.valueOf(d2);
        System.out.println(b1.add(b2));
    }

The output result is 3.3.
We can look at the source code:

    public static BigDecimal valueOf(double val) {
        return new BigDecimal(Double.toString(val));
    }

You can see that BigDecimal.valueOf(double) is actually equivalent to new BigDecimal(String).

So in the future, you must pay attention to accuracy when using BigDecimal for calculations, otherwise it will not only be a matter of a few decimal points when the company loses millions or tens of millions, and needs to be killed, sad. .

Guess you like

Origin blog.csdn.net/noaman_wgs/article/details/94548021