In Java, float/Double Floating Point Numbers are inaccurate (that is, the problem of increasing the number of decimal places)

Source: Internet
Author: User
Today, we encountered a problem in numerical calculation. The program is as follows:
double a = (3.3-2.4)/0.1;

System. Out. println ();
You may think the result is very simple, not 9. Actually, the result is 8.999999998. Why? I read some documents and finally found out the reason.
Why do floating point numbers lose precision?
The binary representation of the decimal number may not be accurate enough.

It is not uncommon for floating-point numbers or double-precision floating-point numbers to be accurately expressed. The reason why floating point values cannot be accurately expressed in decimal format is that the CPU represents a floating point number. In this way, you may sacrifice some precision, and some floating point operations may introduce errors. For example, the binary representation of 2.4 is not the exact 2.4. Instead, the most close binary representation is 2.3999999999999999. The reason is that floating point numbers are composed of two parts: the index and the ending number. The value of a floating point is calculated by a specific mathematical formula. You may encounter loss of precision in any operating system or programming environment.
Note: You can use the binary coded decimal (BCD) library to maintain accuracy. The BCD numeric encoding method separately encodes each decimal digit.
Type Mismatch
You may have mixed floating-point numbers and double-precision floating-point numbers. Make sure that all data types are the same during mathematical operations.
Note: Float variables have only 7-bit precision, while double variables have 15-bit precision.
How to calculate the floating point precision?
The float and double types of simple floating point numbers in Java cannot be computed. Not only Java, but also in many other programming languages. In most cases, the calculation results are accurate, but you can try multiple times (you can do a loop) to try out errors similar to the above. Now we finally understand why we need BCD code.
This problem is quite serious. If you have 9.999999999999 yuan, your computer will not think you can buy 10 yuan of goods.
Some Programming Languages provide special currency types to handle this situation, but Java does not. Now let's take a look at how to solve this problem.

Rounding
Our first response was rounding. The round method in the math class cannot be set to retain a few decimal places. We can only keep two places like this ):
Public double round (double value ){
Return math. Round (value * 100)/100.0;
}
Unfortunately, the code above does not work normally. If you pass 4.015 to this method, it will return 4.01 instead of 4.02, as we can see above
4.015*100 = 401.49999999999994
Therefore, if we want to perform precise rounding, we cannot use simple types for any operation.
Java. Text. decimalformat cannot solve this problem either:
System. Out. println (New java. Text. decimalformat ("0.00"). Format (4.025 ));
The output is 4.02

Bigdecimal
This principle is also mentioned in objective java. Float and double can only be used for scientific computing or engineering computing. In commercial computing, java. Math. bigdecimal is used. Bigdecimal has a total of four creation methods. We don't care about the two that can be created using biginteger. There are two other methods:
Bigdecimal (double Val)
Translates a double into a bigdecimal.
Bigdecimal (string Val)
Translates the string repre sentation of a bigdecimal into a bigdecimal.
The Brief description of the above API is quite clear, and it is usually easier to use the above one. We may use it if we don't want it. What's the problem? When a problem occurs, the detailed description of the above method is as follows:
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. 1000000000000000055511151231257827021181583404541015625. 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 nonwithstanding.

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.
It turns out that if we need precise calculation, we have to use string to create bigdecimal! The example in objective Java uses string to create bigdecimal, but this is not emphasized in the book. This may be a small mistake.

Solution
Now we can solve this problem. The principle is to use bigdecimal and must use string to create it.
But imagine, if we want to do an addition operation, we need to first convert two floating point numbers into strings, and then convert them into bigdecimal. Call the add method on one of them and input another as the parameter, then convert the result of the operation (bigdecimal) to a floating point number. Can you endure this cumbersome process? The following provides a tool class Arith to simplify operations. It provides the following static methods, including addition, subtraction, multiplication, division, 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 cannot perform exact operations on floating point numbers, this tool class provides precise * real floating point operations, including addition, subtraction, multiplication, division, and rounding. */Public class Arith {// default Division calculation precision: Private Static final int def_div_scale = 10; // This class cannot be instantiated private Arith () {}/*** provides precise addition operations. * @ Param V1 add count * @ Param V2 add count * @ return 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 precise subtraction. * @ Param V1 subtrahend * @ Param V2 subtrahend * @ Return Difference Between 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 exact multiplication. * @ Param V1 multiplier * @ Param V2 multiplier * @ return 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 ();}/*** provides (relatively) Precise Division operations. In case of Division, the Division is accurate to * 10 digits after the decimal point, and the subsequent digits are rounded down. * @ Param V1 divisor * @ Param V2 divisor * @ return two parameter vendors */public static double Div (double V1, double V2) {return Div (V1, V2, def_div_scale);}/*** provides (relatively) accurate Division operations. In case of division, the scale parameter determines * the precision, and the number is rounded down. * @ Param V1 divisor * @ Param V2 divisor * @ Param scale indicates the number of digits after the decimal point. * @ Return operator of 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 ();}/*** provides precise rounded decimal places. * @ Param v the number to be rounded off * @ Param scale the number of digits to be retained after the decimal point * @ return the result after rounding */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 ();}}

For double D = 2.4;
System. Out. println (d); // output 2.4, but not 2.3999999999999999?
After reading some materials, when a single double-type value is output, it can be correctly displayed in decimal format. Why? I am not familiar with it, but I am doing floating point computing, floating-point calculation refers to the calculation of floating-point numbers. This operation is usually accompanied by an approximate or rounding because it cannot be accurately expressed. It may be related to floatingdecimal (d). tojavaformatstring () of the double. tostring method.

I guess it is about 2.3999999999999999 that exceeds the output precision, so the reason is truncated.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.