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
- Use BigDecimal(String val) for the amount calculation whenever possible.
- 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.