Double vs. BigDecimal?

前端 未结 6 2205
梦谈多话
梦谈多话 2020-11-22 02:19

I have to calculate some floating point variables and my colleague suggest me to use BigDecimal instead of double since it will be more precise. Bu

相关标签:
6条回答
  • 2020-11-22 02:26

    If you write down a fractional value like 1 / 7 as decimal value you get

    1/7 = 0.142857142857142857142857142857142857142857...
    

    with an infinite sequence of 142857. Since you can only write a finite number of digits you will inevitably introduce a rounding (or truncation) error.

    Numbers like 1/10 or 1/100 expressed as binary numbers with a fractional part also have an infinite number of digits after the decimal point:

    1/10 = binary 0.0001100110011001100110011001100110...
    

    Doubles store values as binary and therefore might introduce an error solely by converting a decimal number to a binary number, without even doing any arithmetic.

    Decimal numbers (like BigDecimal), on the other hand, store each decimal digit as is (binary coded, but each decimal on its own). This means that a decimal type is not more precise than a binary floating point or fixed point type in a general sense (i.e. it cannot store 1/7 without loss of precision), but it is more accurate for numbers that have a finite number of decimal digits as is often the case for money calculations.

    Java's BigDecimal has the additional advantage that it can have an arbitrary (but finite) number of digits on both sides of the decimal point, limited only by the available memory.

    0 讨论(0)
  • 2020-11-22 02:29

    If you are dealing with calculation, there are laws on how you should calculate and what precision you should use. If you fail that you will be doing something illegal. The only real reason is that the bit representation of decimal cases are not precise. As Basil simply put, an example is the best explanation. Just to complement his example, here's what happens:

    static void theDoubleProblem1() {
        double d1 = 0.3;
        double d2 = 0.2;
        System.out.println("Double:\t 0,3 - 0,2 = " + (d1 - d2));
    
        float f1 = 0.3f;
        float f2 = 0.2f;
        System.out.println("Float:\t 0,3 - 0,2 = " + (f1 - f2));
    
        BigDecimal bd1 = new BigDecimal("0.3");
        BigDecimal bd2 = new BigDecimal("0.2");
        System.out.println("BigDec:\t 0,3 - 0,2 = " + (bd1.subtract(bd2)));
    }
    

    Output:

    Double:  0,3 - 0,2 = 0.09999999999999998
    Float:   0,3 - 0,2 = 0.10000001
    BigDec:  0,3 - 0,2 = 0.1
    

    Also we have that:

    static void theDoubleProblem2() {
        double d1 = 10;
        double d2 = 3;
        System.out.println("Double:\t 10 / 3 = " + (d1 / d2));
    
        float f1 = 10f;
        float f2 = 3f;
        System.out.println("Float:\t 10 / 3 = " + (f1 / f2));
    
        // Exception! 
        BigDecimal bd3 = new BigDecimal("10");
        BigDecimal bd4 = new BigDecimal("3");
        System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4)));
    }
    

    Gives us the output:

    Double:  10 / 3 = 3.3333333333333335
    Float:   10 / 3 = 3.3333333
    Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion
    

    But:

    static void theDoubleProblem2() {
        BigDecimal bd3 = new BigDecimal("10");
        BigDecimal bd4 = new BigDecimal("3");
        System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4, 4, BigDecimal.ROUND_HALF_UP)));
    }
    

    Has the output:

    BigDec:  10 / 3 = 3.3333 
    
    0 讨论(0)
  • 2020-11-22 02:30

    There are two main differences from double:

    • Arbitrary precision, similarly to BigInteger they can contain number of arbitrary precision and size
    • Base 10 instead of Base 2, a BigDecimal is n*10^scale where n is an arbitrary large signed integer and scale can be thought of as the number of digits to move the decimal point left or right

    The reason you should use BigDecimal for monetary calculations is not that it can represent any number, but that it can represent all numbers that can be represented in decimal notion and that include virtually all numbers in the monetary world (you never transfer 1/3 $ to someone).

    0 讨论(0)
  • 2020-11-22 02:40

    BigDecimal is Oracle's arbitrary-precision numerical library. BigDecimal is part of the Java language and is useful for a variety of applications ranging from the financial to the scientific (that's where sort of am).

    There's nothing wrong with using doubles for certain calculations. Suppose, however, you wanted to calculate Math.Pi * Math.Pi / 6, that is, the value of the Riemann Zeta Function for a real argument of two (a project I'm currently working on). Floating-point division presents you with a painful problem of rounding error.

    BigDecimal, on the other hand, includes many options for calculating expressions to arbitrary precision. The add, multiply, and divide methods as described in the Oracle documentation below "take the place" of +, *, and / in BigDecimal Java World:

    http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html

    The compareTo method is especially useful in while and for loops.

    Be careful, however, in your use of constructors for BigDecimal. The string constructor is very useful in many cases. For instance, the code

    BigDecimal onethird = new BigDecimal("0.33333333333");

    utilizes a string representation of 1/3 to represent that infinitely-repeating number to a specified degree of accuracy. The round-off error is most likely somewhere so deep inside the JVM that the round-off errors won't disturb most of your practical calculations. I have, from personal experience, seen round-off creep up, however. The setScale method is important in these regards, as can be seen from the Oracle documentation.

    0 讨论(0)
  • 2020-11-22 02:41

    A BigDecimal is an exact way of representing numbers. A Double has a certain precision. Working with doubles of various magnitudes (say d1=1000.0 and d2=0.001) could result in the 0.001 being dropped alltogether when summing as the difference in magnitude is so large. With BigDecimal this would not happen.

    The disadvantage of BigDecimal is that it's slower, and it's a bit more difficult to program algorithms that way (due to + - * and / not being overloaded).

    If you are dealing with money, or precision is a must, use BigDecimal. Otherwise Doubles tend to be good enough.

    I do recommend reading the javadoc of BigDecimal as they do explain things better than I do here :)

    0 讨论(0)
  • 2020-11-22 02:43

    My English is not good so I'll just write a simple example here.

        double a = 0.02;
        double b = 0.03;
        double c = b - a;
        System.out.println(c);
    
        BigDecimal _a = new BigDecimal("0.02");
        BigDecimal _b = new BigDecimal("0.03");
        BigDecimal _c = _b.subtract(_a);
        System.out.println(_c);
    

    Program output:

    0.009999999999999998
    0.01
    

    Somebody still want to use double? ;)

    0 讨论(0)
提交回复
热议问题