Accurate calculation of floating point numbers in Java

zhaozj2021-02-11  183

statement of problem:

What will I see if we can compile this 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 don't have a mistake! The result is indeed

0.06000000000000000005

0.580000000000000001

401.49999999999994

1.2329999999999999

Simple floating point types in Java FLOAT and DOUBLE cannot be operated. Not only Java, there is such a problem in many other programming languages. In most cases, the result of the calculation is accurate, but it can be tried several times (can be a loop) to try the above errors. Now I finally understand why there is a BCD code.

This problem is quite serious, if you have 9.99999999999 yuan, your computer will not think you can purchase 10 yuan.

This situation is provided in a programming language to handle this situation, but Java is not. Let us now look at how to solve this problem.

rounding

Our first reaction is to do four. The Round method in the MATH class cannot be set to keep several decimals, we can only like this (reserve two digits):

Public Double Round (Double Value) {

Return Math.Round (Value * 100) /100.0;

}

Unfortunately, the above code does not work properly, introduced to this method into 4.015 it will return 4.01 instead of 4.02, as we see above

4.015 * 100 = 401.4999999999994

So if we have to do exactly four, you can't use your simple type to do any operation.

Java.text.DecimalFormat can't 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 "Effective Java", float and double can only be used to make scientific calculations or project calculations, we have to use java.math.bigDecimal in business calculations. BigDecimal has four enough way to make a method, we don't care about it with Biginteger, then there are two, they are:

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 quite clear, and usually, it is convenient to use the one of the above. We may want to use it, what is the problem? When I got out of the problem, I found that there is such a detailed description of the following method:

Note:. The results of this constructor can be somewhat unpredictable One might assume that new BigDecimal (.1) is exactly equal to .1, but it is actually equal to .1000000000000000055511151231257827021181583404541015625 This is so because .1 can not 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 to .1, appearances nonwithstanding.The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal ( ". 1") is exactly equal to .1, as one would expect Therefore, it is generally recommended that the (String) constructor be used in preference to this one..

It turns out that if we need to calculate it, you have to use String to make BigDecimal! The example in the "Effective Java" book is to make BigDecimal with String, but the book is not emphasized, this may be a small mistake.

solution

Now we can solve this problem, the principle is to use BigDecimal and must use String to make it.

But imagine it, if we have to do an additional operation, you need to turn the two floating point numbers to string, then make BigDecimal, one of them, to send another as a parameter, then put the calculation result ( BigDecimal is converted to floating point. Can you endure such a cumbersome process? Below we provide a tool class Arith to simplify operation. It provides the following static methods, including addition and subtraction and removal 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.com .java:

Import java.math.bigdecimal;

/ **

* Because Java's simple type is not accurate to calculate floating point number, this tool class provides fine

* The independent floating point calculation, including addition and subtraction and removal and rounding.

* /

Public class arith {

// Default division operation accuracy

Private static final int dev_div_scale = 10;

// This class cannot be instantiated

private arch () {

}

/ **

* Provide precise addition operations.

* @Param V1 is added * @PARAM V2 plus number

* @return 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 ();

}

/ **

* Provide precise subtraction operations.

* @Param V1 is reduced

* @PARAM V2 reduction

* @return 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 ();

}

/ **

* Provide precise multiplication.

* @Param V1 is multiplied

* @Param V2 multiplier

* @Return's product

* /

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 (relatively) accurate division operation, accurate to

* After the decimal point, the number will be rounded in the future.

* @PARAM V1 is divided

* @Param V2 divisor

* @Return two parameters

* /

Public Static Double Div (Double V1, Double V2) {

Return Div (V1, V2, DEF_DIV_SCALE);

}

/ **

* Provide (relatively) precise division operations. When an endless case occurs, the scale parameter refers to

* Define accuracy, the number of numbers in the future.

* @PARAM V1 is divided

* @Param V2 divisor

* @Param Scale means that several digits are required to be accurate to the decimal point.

* @Return two parameters

* /

Public Static Double Div (Double V1, Double V2, INT Scale) {

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, bigdecimal.round_half_up) .doubleValue ();

}

/ **

* Provide precise decimal rounds.

* @Param V Needs a number of four rounds

* @Param scale reserves a few times

* @Return's result * /

Public Static Double Round (double v, int scale) {

IF (scale <0) {

Throw new IllegalargumentException

"The Scale Must Be 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 ();

}

}

转载请注明原文地址:https://www.9cbs.com/read-5148.html

New Post(0)