Title in Java accurate calculation of floating-point number Ayellow (original) modification
Keyword Java floating-point number accurate calculation
The question is raised:
What will we see if we compile and run the following program?
public class test{
public static void Main (String args[]) {
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);
}
};
You're not mistaken! The result is indeed
0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999
Simple floating-point type float and double in Java are not capable of operation. It's not just Java, it's also a problem in many other programming languages. In most cases, the result of the calculation is accurate, but try a few more times (you can do a loop) to try out a similar error. Now we finally understand why we have BCD code.
This problem is quite serious, if you have $9.999999999999, your computer is not going to think you can buy 10 dollars of merchandise.
Special currency types are available in some programming languages to handle this situation, but Java does not. Now let's look at how to solve this problem.
Rounded
Our first reaction was to do rounding. The round method in the math class cannot be set to retain several decimals, and we can only do this (keep two bits):
Public double round (double value) {
Return Math.Round (value*100)/100.0;
}
Unfortunately, the above code does not work properly, passing 4.015 to this method it will return 4.01 instead of 4.02, as we saw above
4.015*100=401.49999999999994
So if we're going to do exact rounding, we can't do anything with a simple type.
Java.text.DecimalFormat also does not solve this problem:
System.out.println (New Java.text.DecimalFormat ("0.00"). Format (4.025));
Output is 4.02
BigDecimal
This principle is also mentioned in the book "Effective Java", where float and double can only be used for scientific calculations or engineering calculations, and we use java.math.BigDecimal in business calculations. There are 4 bigdecimal, we do not care about the two that are made with BigInteger, and then there are two of them:
BigDecimal (double val)
Translates a double into a BigDecimal.
BigDecimal (String val)
Translates the String repre sentation of a BigDecimal into a BigDecimal.
The above API brief description is fairly clear, and usually, the one above is easier to use. We may not want to use it, what is the problem? When there is a problem, only to find out which one of the above-mentioned detailed description of the method has such a paragraph:
Note:the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal (. 1) is exactly equal to. 1, but it's actually equal to. 1000000000000000055511151231 257827021181583404541015625. Because. 1 cannot be represented exactly as a double (or, for that matter, as a bi nary fraction of any finite length). Thus, the Long value that's being passed in to the constructor are not exactly equal to. 1, appearances nonwithstanding.
The (String) constructor, on the other hand, is perfectly predictable:new BigDecimal (". 1") are exactly equal to. 1, as one would expect. Therefore, it is generally recommended and that the (String) constructor being used in preference to this one.
If we need accurate calculation, it is necessary to use string to build BigDecimal! In "effective Java" the example is to use string to build BigDecimal, but the book does not emphasize this point, this may be a small mistake it.
Solution Solutions
Now we can solve this problem, the principle is to use BigDecimal and must use string to build enough.
But imagine, if we're going to do an addition, we need to convert two floating-point numbers to string, then BigDecimal, call the Add method on one of them, pass in another as a parameter, and then transform the result of the operation (BigDecimal) into a floating-point number. Can you endure such a tedious process? Below we provide a tool class arith to simplify the operation. It provides the following static methods, including subtraction and rounding:
public static double Add (Double v1,double v2)
public static double sub (double v1,double v2)
public static double Mul (Double v1,double v2)
public static double div (Double v1,double v2)
public static double div (double v1,double v2,int scale)
public static double round (double v,int scale)
Appendix
Source file Arith.java:
Import Java.math.BigDecimal;
/**
* Because Java's simple type does not accurately operate on floating-point numbers, this tool class provides fine
* Accurate floating-point arithmetic, including subtraction and rounding.
*/
public class arith{
Default division Operation Precision
private static final int def_div_scale = 10;
This class cannot be instantiated
Private Arith () {
}
/**
* provides accurate addition operations.
* @param v1 Summand
* @param v2 Addend
* @return of two parameters and
*/
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 subtraction operations.
* @param v1 minuend
* @param v2 meiosis
* @return The difference of two parameters
*/
public static double sub (double v1,double v2) {
BigDecimal B1 = new BigDecimal (double.tostring (v1));
BigDecimal b2 = new BigDecimal (double.tostring (v2));
Return B1.subtract (B2). Doublevalue ();
}
/**
* Provides accurate multiplication operations.
* @param v1 by multiplier
* @param v2 Multiplier
* @return The product of two parameters
*/
public static double Mul (Double v1,double v2) {
BigDecimal B1 = new BigDecimal (double.tostring (v1));
BigDecimal b2 = new BigDecimal (double.tostring (v2));
Return b1.multiply (B2). Doublevalue ();
}
/**
* Provide (relative) accurate division operation, when the occurrence of an endless situation, accurate to
* After the decimal point 10 digits, the later numbers are rounded.
* @param v1 Dividend
* @param v2 Divisor
* @return two parameters of the quotient
*/
public static double div (Double v1,double v2) {
Return Div (V1,v2,def_div_scale);
}
/**
* Provide (relative) accurate division operations. When an exception occurs, the scale parameter refers to the
* Fixed precision, after which the numbers are rounded.
* @param v1 Dividend
* @param v2 Divisor
* @param scale indicates the need to be accurate to several decimal places.
* @return two parameters of the quotient
*/
public static double div (double v1,double v2,int scale) {
if (scale<0) {
throw New IllegalArgumentException (
"The scale must is a positive integer or zero");
}
BigDecimal B1 = new BigDecimal (double.tostring (v1));
BigDecimal b2 = new BigDecimal (double.tostring (v2));
Return B1.divide (B2,SCALE,BIGDECIMAL.ROUND_HALF_UP). Doublevalue ();
}
/**
* Provides precise rounding of decimal digits.
* @param v need to be rounded to the number
* Retain several @param scale decimal points
* Results after rounding @return
*/
public static double round (double V,int scale) {
if (scale<0) {
throw New IllegalArgumentException (
"The scale must is a positive integer or zero");
}
BigDecimal B = New BigDecimal (double.tostring (v));
BigDecimal one = new BigDecimal ("1");
Return B.divide (ONE,SCALE,BIGDECIMAL.ROUND_HALF_UP). Doublevalue ();
}
}
On the arithmetic precision of double type in Java