(1) Precise Floating Point Calculation
There has always been a problem in the Three-flow Integration Project of Shengli Oilfield, that is, the amount of materials and the actual amount calculated in each report must be a fraction of the money, which is different from the actual amount, this makes customers feel uncomfortable because we use the Double Floating Point Type of Java to define the material amount, and we often need to perform some operations in report statistics, however, the calculation of Floating Point Numbers (double and float) in Java is not accurate. See the following example:
- System. Out. println (0.05 + 0.01 );
- System. Out. println (1.0-0.42 );
- System. Out. println (4.015*100 );
- System. Out. println (123.3/100 );
Copy code
What is your expected output? The actual output is as follows:
- 0.060000000000000005
- 0.5800000000000001
- 401.49999999999994
- 1.2329999999999999
Copy code
This problem is very serious. If you have 123.3 yuan to buy goods, but the computer thinks you only have 123.29999999999999 yuan, the money is not enough, and the computer refuses to trade.
(2) Rounding
Can it be rounded in? Of course we can. In our habits, we will think like this, But rounding means an error, and commercial operations may mean an error. At the same time, Java does not provide a rounding method that retains the specified number of digits, only one math is provided. round (double D) and math. the round (float f) method returns the long integer and integer values respectively. The round method cannot be set to retain a few decimal places. We can only keep two digits as follows ):
- Public double round (double value ){
- Return math. Round (value * 100)/100.0;
- }
Copy code
But unfortunately, the above Code does not work normally. If you pass 4.015 to this method, it will return 4.01 instead of 4.02, as shown in the preceding figure.
- 4.015*100 = 401.49999999999994
Copy code
Therefore, this method cannot meet our requirements for accurate rounding.
Another method is to use Java. text. decimalformat, but there are also problems. The format adopts the round_half_down round mode (the round mode is described below). For example, if 4.025 is reserved, the two decimal places will be 4.02, because. 025 distance from "Nearest Neighbor "(. 02 and. 03) the length is equal, and the rounding down is. 02. If it is 4.0251, the two decimal places reserved are 4.03.
- System. Out. println (New java. Text. decimalformat ("0.00"). Format (4.025 ));
- System. Out. println (New java. Text. decimalformat ("0.00"). Format (4.0251 ));
Copy code
The output is
- 4.02
- 4.03
Copy code
(3) floating point output (Scientific note)
If the Java floating point value is greater than 9999999.0, it is automatically converted to scientific notation. Let's look at the following example:
- System. Out. println (999999999.04 );
- System. Out. println (99999999.04 );
- System. Out. println (10000000.01 );
- System. Out. println (9999999.04 );
Copy code
The output result is as follows:
- 9.9999999904e8
- 9.999999904e7
- 1.20.00001e7
- 9999999.04
Copy code
However, sometimes we do not need to use the scientific notation to convert it into a string, and cannot directly use tostring () or other methods to convert it.
About bigdecimal
Bigdecimal is a constant, any-precision signed decimal number object provided by Java. It provides four constructors, two of which are constructed using biginteger, which we don't care about here, we will focus on the two constructors constructed with double and string (for more information about biginteger, see j2se API documentation ).
- Bigdecimal (double Val)
- Translates a double into a bigdecimal.
- Bigdecimal (string Val)
- Translates the string representation of a bigdecimal into a bigdecimal.
Copy code
Bigdecimal (double) refers to the construction of a double type decimal number as a bigdecimal object instance.
Bigdecimal (string) is to construct a bigdecimal object represented by string as a bigdecimal object instance.
In practice, we define floating point numbers as double or float, but the bigdecimal (double) API document has the following sentence:
Note: The results of this constructor can be somewhat unpredictable. one might assume that new bigdecimal (. 1) is exactly equal. 1, but it is actually equal. 10000000000000000555111512312578 27021181583404541015625. this is so because
. 1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length ). thus, the long value that is being passed in to the constructor is not exactly equal. 1, appearances notwithstanding.
The (string) constructor, on the other hand, is perfectly predictable: New bigdecimal (". 1 ") is exactly equal. 1, as one wocould CT. therefore, it is generally recommended that the (string) constructor be used in preference to this one
The following is a simple explanation of this passage:
Note: The constructor may have unpredictable results. Someone may imagine new bigdecimal (. 1) equal. 1 is correct, but it is actually equal. 1000000000000000055511151231257827021181583404541015625, that's why. 1 cannot be exactly represented by a double. Therefore, the long value put into the constructor is not exactly equal. 1, although the appearance looks equal.
However, the (string) constructor is completely predictable. New bigdecimal (". 1 ") exact equals as expected. 1. Therefore, the (string) constructor is recommended first.
See the following results:
- System. Out. println (New bigdecimal (123456789.02). tostring ());
- System. Out. println (New bigdecimal ("123456789.02"). tostring ());
Copy code
Output:
- 123456789.01999999582767486572265625
- 123456789.02
Copy code
Now we know that if exact calculation is required, you must use string to create bigdecimal!
Implementation Scheme
Now we know how to solve this problem. In principle, we use the bigdecimal (string) constructor. We recommend that you use data involving floating point numbers such as amounts in commercial application development, all are defined as string, which can be defined as character fields in the database. When you need to use this data for computation, use bigdecimal (string) to construct a bigdecimal object for computation to ensure accurate data computation. It also avoids the emergence of scientific notation. If scientific notation is not a burden in applications, you can consider defining it as a floating point type.
Here we provide a tool class that defines the addition, subtraction, multiplication, division, and rounding of floating point numbers. For reference.
Source File mathextend. Java:
- Import java. Math. bigdecimal;
- Public class mathextend
- {
- // Default division operation precision
- Private Static final int default_div_scale = 10;
- /**
- * Provides precise addition operations.
- * @ Param V1
- * @ Param v2
- * @ Return the sum of the two parameters
- */
- Public static double add (double V1, double V2)
- {
- Bigdecimal b1 = new bigdecimal (double. tostring (V1 ));
- Bigdecimal b2 = new bigdecimal (double. tostring (V2 ));
- Return b1.add (B2). doublevalue ();
- }
- /**
- * Provides accurate addition operations
- * @ Param V1
- * @ Param v2
- * @ Return mathematical Addition of two parameters, returned in string format
- */
- Public static string add (string V1, string V2)
- {
- Bigdecimal b1 = new bigdecimal (V1 );
- Bigdecimal b2 = new bigdecimal (V2 );
- Return b1.add (B2). tostring ();
- }
- /**
- * Provides precise subtraction operations.
- * @ Param V1
- * @ Param v2
- * @ Return Difference Between Two Parameters
- */
- Public static double subtract (double V1, double V2)
- {
- Bigdecimal b1 = new bigdecimal (double. tostring (V1 ));
- Bigdecimal b2 = new bigdecimal (double. tostring (V2 ));
- Return b1.subtract (B2). doublevalue ();
- }
- /**
- * Provides precise subtraction.
- * @ Param V1
- * @ Param v2
- * @ Return: returns the mathematical difference between two parameters in string format.
- */
- Public static string subtract (string V1, string V2)
- {
- Bigdecimal b1 = new bigdecimal (V1 );
- Bigdecimal b2 = new bigdecimal (V2 );
- Return b1.subtract (B2). tostring ();
- }
- /**
- * Provides precise multiplication.
- * @ Param V1
- * @ Param v2
- * @ Return the product of two parameters
- */
- Public static double multiply (double V1, double V2)
- {
- Bigdecimal b1 = new bigdecimal (double. tostring (V1 ));
- Bigdecimal b2 = new bigdecimal (double. tostring (V2 ));
- Return b1.multiply (B2). doublevalue ();
- }
- /**
- * Accurate Multiplication
- * @ Param V1
- * @ Param v2
- * @ Return mathematical product of the two parameters, returned in string format
- */
- Public static string multiply (string V1, string V2)
- {
- Bigdecimal b1 = new bigdecimal (V1 );
- Bigdecimal b2 = new bigdecimal (V2 );
- Return b1.multiply (B2). tostring ();
- }
- /**
- * Provides (relatively) accurate Division operations, accurate
- * 10 digits after the decimal point, rounded to the next digit. round_half_even is used for rounding.
- * @ Param V1
- * @ Param v2
- * @ Return parameter vendors
- */
- Public static double divide (double V1, double V2)
- {
- Return divide (V1, V2, default_div_scale );
- }
- /**
- * Provides (relatively) accurate Division operations. In case of division, the scale parameter indicates
- * Set the precision. The number is rounded down. Round_half_even is used for rounding.
- * @ Param V1
- * @ Param v2
- * @ Param scale indicates the number of digits after the decimal point.
- * @ Return parameter vendors
- */
- Public static double divide (double V1, double V2, int scale)
- {
- Return divide (V1, V2, scale, bigdecimal. round_half_even );
- }
- /**
- * Provides (relatively) accurate Division operations. In case of division, the scale parameter indicates
- * Set the precision. The number is rounded down. The rounding mode is specified by the user.
- * @ Param V1
- * @ Param v2
- * @ Param scale indicates the number of digits after the decimal point.
- * @ Param round_mode indicates the rounding mode specified by the user.
- * @ Return parameter vendors
- */
- Public static double divide (double V1, double V2, int scale, int round_mode ){
- If (scale <0)
- {
- Throw new illegalargumentexception ("the scale must be a positive integer or zero ");
- }
- Bigdecimal b1 = new bigdecimal (double. tostring (V1 ));
- Bigdecimal b2 = new bigdecimal (double. tostring (V2 ));
- Return b1.divide (B2, scale, round_mode). doublevalue ();
- }
- /**
- * Provides (relatively) accurate Division operations, accurate
- * 10 digits after the decimal point, rounded to the next digit. round_half_even is used for rounding.
- * @ Param V1
- * @ Param v2
- * @ Return refers to the operator of two parameters, which are returned in string format.
- */
- Public static string divide (string V1, string V2)
- {
- Return divide (V1, V2, default_div_scale );
- }
- /**
- * Provides (relatively) accurate Division operations. In case of division, the scale parameter indicates
- * Set the precision. The number is rounded down. Round_half_even is used for rounding.
- * @ Param V1
- * @ Param v2
- * @ Param scale indicates the number of digits after the decimal point.
- * @ Return refers to the operator of two parameters, which are returned in string format.
- */
- Public static string divide (string V1, string V2, int scale)
- {
- Return divide (V1, V2, default_div_scale, bigdecimal. round_half_even );
- }
- /**
- * Provides (relatively) accurate Division operations. In case of division, the scale parameter indicates
- * Set the precision. The number is rounded down. The rounding mode is specified by the user.
- * @ Param V1
- * @ Param v2
- * @ Param scale indicates the number of digits after the decimal point.
- * @ Param round_mode indicates the rounding mode specified by the user.
- * @ Return refers to the operator of two parameters, which are returned in string format.
- */
- Public static string divide (string V1, string V2, int scale, int round_mode)
- {
- If (scale <0)
- {
- Throw new illegalargumentexception ("the scale must be a positive integer or zero ");
- }
- Bigdecimal b1 = new bigdecimal (V1 );
- Bigdecimal b2 = new bigdecimal (V2 );
- Return b1.divide (B2, scale, round_mode). tostring ();
- }
- /**
- * Provides precise decimal point rounding. The rounding mode adopts round_half_even.
- * @ Param V refers to the number rounded up.
- * @ Param scale: number of digits after the decimal point
- * @ Return returns the result after rounding.
- */
- Public static double round (Double V, int scale)
- {
- Return round (v, scale, bigdecimal. round_half_even );
- }
- /**
- * Provides precise rounding of decimal places.
- * @ Param V refers to the number rounded up.
- * @ Param scale: number of digits after the decimal point
- * @ Param round_mode the specified rounding mode
- * @ Return returns the result after rounding.
- */
- Public static double round (Double V, int scale, int round_mode)
- {
- If (scale <0)
- {
- Throw new illegalargumentexception ("the scale must be a positive integer or zero ");
- }
- Bigdecimal B = new bigdecimal (double. tostring (V ));
- Return B. setscale (scale, round_mode). doublevalue ();
- }
- /**
- * Provides precise decimal point rounding. The rounding mode adopts round_half_even.
- * @ Param V refers to the number rounded up.
- * @ Param scale: number of digits after the decimal point
- * @ Return returns the result after rounding, in string format
- */
- Public static string round (string V, int scale)
- {
- Return round (v, scale, bigdecimal. round_half_even );
- }
- /**
- * Provides precise rounding of decimal places.
- * @ Param V refers to the number rounded up.
- * @ Param scale: number of digits after the decimal point
- * @ Param round_mode the specified rounding mode
- * @ Return returns the result after rounding, in string format
- */
- Public static string round (string V, int scale, int round_mode)
- {
- If (scale <0)
- {
- Throw new illegalargumentexception ("the scale must be a positive integer or zero ");
- }
- Bigdecimal B = new bigdecimal (v );
- Return B. setscale (scale, round_mode). tostring ();
- }
- }
Copy code
Introduction to the bigdecimal round mode:
Bigdecimal defines the rounding mode, which is used only when Division operations or rounding are performed. For more information, see j2se API documentation.
Static int
Round_ceiling
Rounding mode to Round towards positive infinity.
Round to positive infinity
Static int
Round_down
Rounding mode to Round towards zero.
Round to zero
Static int
Round_floor
Rounding mode to Round towards negative infinity.
Round to negative infinity
Static int
Round_half_down
Rounding mode to Round towards "Nearest Neighbor" unless both neighbors are equidistant, in which case round down.
Round to the nearest side, unless the two sides (distance) are equal. If so, round down. For example, 1.55 retains a decimal number and returns 1.5.
Static int
Round_half_even
Rounding mode to Round towards the "Nearest Neighbor" unless both neighbors are equidistant, in which case, Round towards the even neighbor.
Round to the nearest side (distance), unless the two sides (distance) are equal. If so, use round_half_up if the reserved digits are odd. If the reserved digits are even, use round_half_down.
Static int
Round_half_up
Rounding mode to Round towards "Nearest Neighbor" unless both neighbors are equidistant, in which case round up.
Round to the nearest side (distance), unless the two sides (distance) are equal, if so, round up, 1.55 retains a decimal number and returns 1.6
Static int
Round_unnecessary
Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary.
The calculation result is accurate, and the rounding mode is not required.
Static int
Round_up
Rounding mode to round away from zero.
Round to the direction away from 0