Search code examples
javadoubleroundingbigdecimalfloating-point-precision

Why is the behavior of Double.toString not the same as MathContext.DECIMAL64 when constructing a BigDecimal in Java?


To me, it seems like Double should be following the same rules as the IEEE standard used in MathContext.DECIMAL64, however, in this case, I get different behavior:

import java.math.BigDecimal;
import java.math.MathContext;

public class BigDecimalTest {

    public static void main(String[] args) {

        double foo = 8.7;

        System.out.println(new BigDecimal(foo, MathContext.DECIMAL64));
        System.out.println(new BigDecimal(Double.toString(foo)));

    }
}

Output:

8.699999999999999
8.7

Solution

  • A BigDecimal Arbitrary-precision signed decimal numbers. On the other hand a double is a floating point number and therefore it implies some impressision on the number that is represented.

    When you try to create a Bigdecimal from a double you get the value represented by de double that in your example is 8.699999999999999. The reason is that the double variable is not really able to keep exactly 8.7. More info about big decimals on

    http://goo.gl/tRMLhA

    When you represent the number as a String "8.7" value is returned by the to string method and consecuentlly thats the value asigned to the Bigdecimal. Method toString use some rools to represent a double value as a String take a look to

    http://docs.oracle.com/javase/7/docs/api/java/lang/Double.html#toString(double)&bwsCriterion=toString&bwsMatch=3

    When you use MathContext.DECIMAL64 you are saying "ok I know that there is some imperfection on double number so. I want you to make rounding with a presicion of 16 digit and Round mode half up" And thats exactly what you get. Java take the double representation of 8.7 that is 8.6999999999999993 and round it to 16 digits the result is 8.699999999999999 cause the digit number 17 was a "3" and therefore the result of rounding half up is leave digit number 16 as it is that is a 9.

    Info about the meaning of MathContext.DECIMAL64 on: http://goo.gl/fVUehh

    If you want to get 8.7 just use a precission lower than 16 digit.

    Example code

    /**
         * @param args
         */
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
               double foo = 8.7;
    
                System.out.println(new BigDecimal(foo, MathContext.DECIMAL64));
                System.out.println(new BigDecimal(foo, new MathContext(16)));
                System.out.println(new BigDecimal(foo, new MathContext(17)));
    
                System.out.println(new BigDecimal(foo, new MathContext(1)));
                System.out.println(new BigDecimal(foo, new MathContext(2)));
                System.out.println(new BigDecimal(foo, new MathContext(3)));
                System.out.println(new BigDecimal(foo, new MathContext(15)));
    
                System.out.println(new BigDecimal(Double.toString(foo)));
    
        }
    

    Output

    8.699999999999999
    8.699999999999999
    8.6999999999999993
    9
    8.7
    8.70
    8.70000000000000
    8.7
    

    more information aboutBigDecimal and doubles on

    https://blogs.oracle.com/CoreJavaTechTips/entry/the_need_for_bigdecimal