The topic of amount calculation and storage was discussed in the wechat group yesterday, so today I would like to post it.

Classic precision loss problem

The Java types float and double can lose precision when used to do calculations.

public static void main(String[] args) { test1(); test2(); } private static void test1() {double totalAmount = 0.09; Double feeAmount = 0.02; double tradeAmount = totalAmount - feeAmount; System.out.println(tradeAmount); }Copy the code

What is the output of the above program?

0.07? Not too!

The correct result is:

0.06999999999999999
Copy the code

Why is that?

Floating-point numbers can lose precision, and floating-point decimal numbers generally do not have the exact same binary representation, a side effect of the floating-point data representation adopted by the CPU. For this reason, there may be some loss of precision, and some floating-point operations may yield unknown results.

Floating-point operations are rarely precise, and errors occur whenever they exceed the range of precision that can be expressed. Be careful when using a float or double for exact calculations. Unless you can tolerate loss of precision, the resulting error can result in an inconsistent account reconciliation.

How to solve

This principle is also mentioned in the book Effective Java, where floats and doubles should only be used for scientific or engineering calculations, and in business calculations we use java.math.bigdecimal.

BigDecimal is suitable for more precise operations and also provides rich operator types, decimal control, rounding rules, and more.

However, there can be precision loss when using BigDecimal improperly, as in the constructor for double:

BigDecimal(double val)
Copy the code

Here’s another example:

Private static void test2() {double totalAmount = 0.09; Double feeAmount = 0.02; BigDecimal tradeAmount = new BigDecimal(totalAmount).subtract(new BigDecimal(feeAmount)); System.out.println(tradeAmount); }Copy the code

Output:

0.0699999999999999962529972918900966760702431201934814453125
Copy the code

The accuracy is even more terrifying.

So, be sure to use the String constructor:

BigDecimal(String val)
Copy the code
Private static void test3() {double totalAmount = 0.09; Double feeAmount = 0.02; BigDecimal tradeAmount = new BigDecimal(String.valueOf(totalAmount)) .subtract(new BigDecimal(String.valueOf(feeAmount))); System.out.println(tradeAmount); }Copy the code

conclusion

  1. Use BigDecimal(String val) for the amount calculation whenever possible.
  2. The database stores the amount, generally has the integer type and the floating point type two kinds of storage. If there is conversion, it is recommended to use a floating point decimal for storage, which allows flexible control of precision. Decimal directly corresponds to the Java type BigDecimal. And, of course, you can store fractions as integers, so you can transfer money in dollars and if you forget to convert it into dollars, that’s a tragedy.

Recommended reading

Dry goods: Free 2TB architect four-stage video tutorial

Interview: the most complete Java multithreaded interview questions and answers

Tools: Recommended an online creation flow chart, mind mapping software

Share Java dry goods, high concurrency programming, hot technology tutorials, microservices and distributed technology, architecture design, blockchain technology, artificial intelligence, big data, Java interview questions, and cutting-edge hot news.