Problem description
When using Java to calculate floating point numbers in the project, it was found that for calculations like 4.015*100, the result was not the expected 401.5, but 401.499999999999999999999999999999994. Such a long number of digits is unfriendly for display.
Cause of the problem: floating point number representation
After consulting relevant information, I found that the reason is that the floating point numbers in the computer cannot be fully expressed accurately. For example, for a double type 38414.4, the computer stores it like this:
Convert to binary: 100101100001110.01100110011001100110011001100110011001100
Transform to a subject
Learn counting method: 1.0010110000111001100110011001100110011001100110011001100110011001100×2^15
The double encoding format is as follows:
double sign bit 1 digit stage code 11 digit mantissa 52 digits
Symbol bit: positive number is 0
Order code: 15 is a positive number, so the highest bit is 1, the lowest bit is minus 1, which is 10000001110
Mantissue: Remove the default 1 of the highest bit, which is 0010110000111001100110011001100110011001100110011001100
Combined, the final encoding is: 0 1000001110 001011000011100110011001100110011001100110011001100110011001100
From here, we can see that the main reason is that binary encoding makes the fractional part impossible to fully express exactly, such as 0.4 = 0.25 + 0.125 + ..., which can only be infinitely close. Therefore, accuracy errors will occur when calculating floating point numbers.
Solution: High precision
BigDecimal in Java can support floating point number operations with arbitrary precision. In the book "Effective Java", it is recommended that float and double are used for scientific calculations or engineering calculations, while java.math.BigDecimal is used in commercial calculations.
There are many construction methods for BigDecimal, such as BigDecimal(double) and BigDecimal(String). It should be noted that the construction parameters are String type to ensure that accuracy is not lost, because the double type itself is incompletely accurate. Therefore, it needs to be written as follows: BigDecimal("0.02").
Basic operations of double type can be found in BigDecimal. In addition, BigDecimal can also be used for formatting output with NumberFormat.
BigDecimal will generate new BigDecimal objects when doing operations, so it will bring more performance overhead compared to double.
A preliminary study on high-precision implementation
So how does BigDecimal be able to represent arbitrary precision? Here is only a preliminary analysis.
First, let’s look at the implementation of BigInteger. The ordinary int type is 32 bits, so there is a range limitation. BigInteger has the member variable int[] mag, so that the longer int array makes it possible to represent integers of any size.
Let’s look at the implementation of BigDecimal. Its official introduction says that any BigDecimal can be represented as unscaledValue × 10^-scale. unscaledValue is an integer of any size, corresponding to the member variable BigInteger intVal in the source code; scale is the order, corresponding to the variable int scale in the source code. This way, BigDecimal is implemented based on BigInteger.
The above is the solution to the floating point accuracy problem in Java introduced to you by the editor. I hope it will be helpful to you. If you have any questions, please leave me a message.