problem
Today, the teacher asked a question: how much is 9.8 - 0.1? It's a very simple question, but it hides a very big problem. If you don't pay attention, step on the pit. The code is as follows
double a = 9.8; double b = 0.1; System.out.println(a+b); // 9.9 System.out.println(a-b); // 9.700000000000001 System.out.println(a*b); // 0.9800000000000001 System.out.println(a/b); // 9.9
Why isn't the subtraction answer 9.7? This involves the underlying computing principle of the computer
Data exists in binary form in computer memory. Decimal numbers can be accurately converted when converted into binary numbers, but floating-point numbers can't, for example
0.98 = 0.111110101110000101000111101011100001010001111010111
Therefore, there is a high probability that the precision of floating-point numbers will be lost during conversion, which is why 9.8 - 0.1= nine point seven
So after knowing this pit, how can we avoid this problem?
Java provides us with a class for this purpose
BigDecimal
1. Introduction
The API class BigDecimal provided by Java in the java.math package is used to accurately calculate the number of significant bits exceeding 16 bits. Double, a double precision floating-point variable, can handle 16 bit significant numbers. In practical applications, larger or smaller numbers need to be calculated and processed. float and double can only be used for scientific calculation or engineering calculation. java.math.BigDecimal should be used in business calculation. BigDecimal creates objects. We cannot use traditional arithmetic operators such as +, -, *, / to directly perform mathematical operations on their objects, but must call their corresponding methods. The parameter in the method must also be an object of BigDecimal. Constructors are special methods of classes that are specifically used to create objects, especially objects with parameters.
2. Construction method
BigDecimal(int) // Creates an object with the integer value specified by the parameter BigDecimal(double) // Creates an object with the double value specified by the parameter (not recommended) BigDecimal(long) // Creates an object with the long integer value specified by the parameter BigDecimal(String) // Create an object with the value specified by the parameter as a string (recommended)
3. Some methods
add(BigDecimal) // Add subtract(BigDecimal) // subtract multiply(BigDecimal) // Multiply divide(BigDecimal) // be divided by
4. Simple use
double a = 9.8; double b = 0.1; BigDecimal x = new BigDecimal(Double.toString(a)); // Constructor using String type BigDecimal y = new BigDecimal(Double.toString(b)); System.out.println(x.add(y)); // 9.9 System.out.println(x.subtract(y)); // 9.7 System.out.println(x.multiply(y)); // 0.98 System.out.println(x.divide(y)); // 9.7
You can see that Java encapsulates such a useful class for us. Later, when we need to operate on floating-point numbers, we just need to use it directly
5. Some issues
1. Why is the double type construction method not recommended
Let's look at the code first
double a = 9.8; double b = 0.1; BigDecimal x = new BigDecimal(a); // Constructor using double type BigDecimal y = new BigDecimal(b); System.out.println(x); // 9.800000000000000710542735760100185871124267578125 System.out.println(y); // 0.1000000000000000055511151231257827021181583404541015625
It can be seen that the problem of precision loss occurs when initializing the object, and the corresponding explanation is given in the API document
1. The result of this constructor may be somewhat unpredictable. Suppose you write new BigDecimal(0.1) in Java to create a BigDecimal, which is completely equal to 0.1 (non scale value is 1, scale is 1), but actually equal to 0.10000000000005551231257827021181583404541015625. This is because 0.1 cannot be represented exactly like double (or as any binary fraction of finite length). Therefore, the value being passed to the construct is not exactly equal to 0.1, although on the surface.
two The String construction, on the other hand, is completely predictable: write new BigDecimal("0.1") to create BigDecimal, which is exactly equal to 0.1, as expected. Therefore, it is generally recommended that String constructor take precedence over this.
three When double must be used as the source for BigDecimal, note that this construct provides an accurate conversion; It does not convert double to String, using the Double.toString(double) method and then using the BigDecimal(String) constructor to get the same result. To get this result, use the static valueOf(double) method.
Therefore, in order to avoid this situation, it is recommended to use the construction method of String type for initialization
2. Possible pit in division operation
BigDecimal division may not be divisible, such as 4.5 / 1.3. In this case, java.lang.arithmetexception: non terminating decimal expansion will be reported; no exact representable decimal result.
In fact, the divide method can pass three parameters: public BigDecimal divide (BigDecimal division, int scale, int rounding mode). The first parameter represents the divisor, the second parameter represents the number of digits reserved after the decimal point, and the third parameter represents the rounding mode. The rounding mode is only used in Division or rounding. There are the following types
// x / y, keep 4 digits after the decimal point, rounding mode System.out.println(x.divide( y ,4, RoundingMode.CEILING) ); // Round to positive infinity System.out.println(x.divide( y ,4, RoundingMode.DOWN) ); // Round to 0 System.out.println(x.divide( y ,4, RoundingMode.FLOOR) ); // Round to negative infinity System.out.println(x.divide( y ,4, RoundingMode.HALF_DOWN) ); // Round to the nearest side. Both sides are the same. Round down System.out.println(x.divide( y ,4, RoundingMode.HALF_EVEN) ); // Round to the nearest side. Both sides are the same. If the reserved bit is an odd number, it will go up and vice versa System.out.println(x.divide( y ,4, RoundingMode.HALF_UP) ); // Round to the nearest side, both sides are the same, round up System.out.println(x.divide( y ,4, RoundingMode.UP) ); // Rounding away from 0 System.out.println(x.divide( y ,4, RoundingMode.UNNECESSARY) ); // The calculation result is accurate and does not need to be rounded. If the division is not complete, an error will still be reported
reference material
1. Baidu Encyclopedia: BigDecimal_ Baidu Encyclopedia