About a BigDecimal in Java that causes double Precision loss & quot; bug & quot;, bigdecimaldouble

Source: Internet
Author: User

In Java, A BigDecimal "bug" that causes double Precision loss, bigdecimaldouble

Background

In the blog's disgusting 0.5 rounding question, I saw a question about 0.5 incorrect rounding. It is mainly said that after double is converted to BigDecimal, the result is not correct after rounding:

public class BigDecimalTest {    public static void main(String[] args){        double d = 301353.05;        BigDecimal decimal = new BigDecimal(d);        System.out.println(decimal);//301353.0499999999883584678173065185546875        System.out.println(decimal.setScale(1, RoundingMode.HALF_UP));//301353.0    }}

The output result is:

301353.0499999999883584678173065185546875
301353.0

This result is obviously not what we expected. What we want is to get 301353.1.

Cause

Allow discerning people to see other problems at a glance --The BigDecimal constructor public BigDecimal (double val) loses the precision of the double parameter.Finally, the error result is returned. The key to the problem is that the BigDecimal constructor public BigDecimal (double val) loses the precision of the double parameter.

Solution

The reason is found above, so it is a good solution. As long as the precision loss from double to BigDecimal is prevented, no problem will occur.

1)It is easy to think of the first solution: Use BigDecimal's String-based constructor: public BigDecimal (String val.

public class BigDecimalTest {    public static void main(String[] args){        double d = 301353.05;        System.out.println(new BigDecimal(new Double(d).toString()));        System.out.println(new BigDecimal("301353.05"));        System.out.println(new BigDecimal("301353.895898895455898954895989"));    }}

Output result:

301353.05
301353.05
301353.895898895455898954895989

We can see that there is no loss of precision, and no error will be made if we are rounding.

2)The BigDecimal constructor public BigDecimal (double val) will lose the precision of the double parameter. This may be counted as a bug in JDK. Since there is a bug, we should solve it. The above method is to bypass it. Now we can convert double to BigDecimal, and ensure thatIn some casesThe double precision is not lost.

Import java. math. bigDecimal; public class BigDecimalUtil {public static BigDecimal doubleToBigDecimal (double d) {String doubleStr = String. valueOf (d); if (doubleStr. indexOf (". ")! =-1) {int pointLen = doubleStr. replaceAll ("\ d + \\. ",""). length (); // The number of digits after the decimal point. pointLen = pointLen> 16? 16: pointLen; // The maximum number of digits after the double decimal point is 16 double pow = Math. pow (10, pointLen );
Long tmp = (long) (d * pow); return new BigDecimal (tmp ). divide (new BigDecimal (pow);} return new BigDecimal (d);} public static void main (String [] args) {// System. out. println (doubleToBigDecimal (301353.05); // System. out. println (doubleToBigDecimal (-301353.05); // System. out. println (doubleToBigDecimal (new Double (-301353.05); // System. out. println (doubleToBigDecimal (301353); // System. out. println (doubleToBigDecimal (new Double (-301353); double d = 301353.05; // 5898895455898954895989; System. out. println (doubleToBigDecimal (d); System. out. println (d); System. out. println (new Double (d ). toString (); System. out. println (new BigDecimal (new Double (d ). toString (); System. out. println (new BigDecimal (d ));}}

Output result:

301353.05
301353.05
301353.05
301353.05
301353.0499999999883584678173065185546875

We have written a tool class above to implement the "No loss" double Precision conversion from double to BigDecimal. To convert a double with a valid number after the decimal point to a double without a valid number after the decimal point, convert it to BigDecimal, and then use the divide of BigDecimal to return the previous size.

The above results seem to be perfect, but they are actually problematic. We also mentioned"In some casesThere is no loss of double Precision ". Let's look at an example first:

    public static void main(String[] args){        double d = 301353.05;        System.out.println(doubleToBigDecimal(d));        System.out.println(d);        System.out.println(new Double(d).toString());        System.out.println(new BigDecimal(new Double(d).toString()));        System.out.println(new BigDecimal(d));                System.out.println("=========================");        d = 301353.895898895455898954895989;        System.out.println(doubleToBigDecimal(d));        System.out.println(d);        System.out.println(new Double(d).toString());        System.out.println(new BigDecimal(new Double(d).toString()));        System.out.println(new BigDecimal(d));        System.out.println(new BigDecimal("301353.895898895455898954895989"));                System.out.println("=========================");        d = 301353.46899434;        System.out.println(doubleToBigDecimal(d));        System.out.println(d);        System.out.println(new Double(d).toString());        System.out.println(new BigDecimal(new Double(d).toString()));        System.out.println(new BigDecimal(d));                System.out.println("=========================");        d = 301353.45789666;        System.out.println(doubleToBigDecimal(d));        System.out.println(d);        System.out.println(new Double(d).toString());        System.out.println(new BigDecimal(new Double(d).toString()));        System.out.println(new BigDecimal(d));    }

Output result:

301353.05
301353.05
301353.05
301353.05
301353.0499999999883584678173065185546875
======================================
301353.89589889544
301353.89589889545
301353.89589889545
301353.89589889545
301353.895898895454593002796173095703125
301353.895898895455898954895989
======================================
301353.46899434
301353.46899434
301353.46899434
301353.46899434
301353.4689943399862386286258697509765625
======================================
301353.45789666
301353.45789666
301353.45789666
301353.45789666
301353.4578966600238345563411712646484375
We can see that we have implemented the doubleToBigDecimal method.Only when there are few digits after the decimal point of double (for example, only 5 or 6 digits) Can the precision be ensured completely without loss..

If the number of digits after the double decimal point is too large, the precision loss will occur in d * pow, and the final result will also be inaccurate. Therefore, if the number of digits after the decimal point is large, it is better to use the BigDecimal String parameter constructor. You can use your own doubleToBigDecimal method only when the number of digits after the decimal point is small.

Because we can see that the last digit of the BigDecimal after the conversion of the original double is 5 and the other is 4, the reason is in the preceding conversion method:

Long tmp = (long) (d * pow );

This step may cause a small loss of precision, because d is a double, d * pow is a double (but after the decimal point is 0, so there is no loss of precision for the conversion to long ), therefore, there will be a small loss of precision (double calculation may always have a loss of precision). However, this loss of precision is not so abrupt compared with the loss of precision of the BigDecimal constructor public BigDecimal (double val) (maybe we write doubleToBigDecimal, for more information, see ).

Summary:

If you need to ensure the accuracy, it is best not to use the BigDecimal double parameter constructor, because there is a possibility of loss of the double parameter precision, it is best to use the BigDecimal String parameter constructor. It is best not to use the BigDecimal double parameter constructor.

 

Postscript:

In fact, this is a BigDecimal bug. The title party is suspected of being a "pitfall" of BigDecimal ".

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.