Double vs. BigDecimal?

时间:2010-08-05 09:39:40

标签: java floating-point double bigdecimal

我必须计算一些浮点变量,我的同事建议我使用BigDecimal代替double,因为它会更准确。但我想知道它是什么以及如何最大限度地利用BigDecimal

8 个答案:

答案 0 :(得分:371)

BigDecimal是表示数字的精确方式。 Double具有一定的精度。使用各种大小的双精度(比如d1=1000.0d2=0.001)可能会导致0.001在求和时全部掉落,因为幅度的差异非常大。使用BigDecimal这不会发生。

BigDecimal的缺点是速度较慢,而且以这种方式编程算法要困难得多(由于+ - *和{{1}没有超载)。

如果您正在处理金钱,或精确是必须的,请使用/。否则BigDecimal往往足够好。

我建议您阅读Doubles的{​​{3}},因为他们会比我更好地解释事情:)

答案 1 :(得分:137)

我的英语不好,所以我在这里写一个简单的例子。

    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);

节目输出:

0.009999999999999998
0.01

有人还想用双? ;)

答案 2 :(得分:41)

与double有两个主要区别:

  • 任意精度,与BigInteger类似,它们可以包含任意精度和大小的数量
  • Base 10而不是Base 2,BigDecimal是n * 10 ^ scale,其中n是任意大的有符号整数,scale可以被认为是向左或向右移动小数点的位数

你应该使用BigDecimal进行货币计算的原因并不是它可以代表任何数字,而是它可以代表所有可以用十进制概念表示的数字,并且几乎包括货币世界中的所有数字(你永远不会转移1) / 3 $给某人)。

答案 3 :(得分:17)

如果您将小数值(如1 / 7)写为十进制值,则

1/7 = 0.142857142857142857142857142857142857142857...

具有无限序列142857。由于您只能编写有限数量的数字,因此不可避免地会引入舍入(或截断)错误。

1/101/100这样的数字表示为带小数部分的二进制数也在小数点后面有无限位数:

1/10 = binary 0.0001100110011001100110011001100110...

Doubles将值存储为二进制,因此可能仅通过将十进制数转换为二进制数来引入错误,甚至不进行任何算术运算。

另一方面,十进制数字(如BigDecimal)按原样存储每个十进制数字。这意味着在一般意义上,十进制类型不比二进制浮点或固定点类型更精确(例如,它不能在不损失精度的情况下存储1/7),但对于具有有限数字的数字,它更准确货币计算的情况通常是小数位数。

Java的BigDecimal还有一个额外的好处,就是它可以在小数点的两边都有一个任意(但有限)的数字位数,仅受可用内存的限制。

答案 4 :(得分:7)

BigDecimal是Oracle的任意精度数值库。 BigDecimal是Java语言的一部分,对于从财务到科学的各种应用程序非常有用(那就是那种类型的应用程序)。

使用双打进行某些计算并没有错。但是,假设您想要计算Math.Pi * Math.Pi / 6,即Riemann Zeta函数的值,其实际参数为2(我目前正在研究的项目)。浮点除法会给你一个痛苦的舍入错误问题。

另一方面,

BigDecimal包含许多用于计算任意精度的表达式的选项。下面的Oracle文档中描述的添加,乘法和除法方法"取代" BigDecimal Java World中的+,*和/:

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

compareTo方法在while和for循环中特别有用。

但是,在使用BigDecimal的构造函数时要小心。在许多情况下,字符串构造函数非常有用。例如,代码

BigDecimal onethird = new BigDecimal(" 0.33333333333");

利用1/3的字符串表示来表示无限重复的数字到指定的准确度。舍入误差最有可能发生在JVM内部的某个深处,以至于舍入误差不会影响大部分实际计算。然而,从个人经验来看,我看到了四舍五入。从Oracle文档中可以看出,setScale方法在这些方面非常重要。

答案 5 :(得分:4)

如果要进行计算,则应遵循有关如何计算以及应使用的精度的法律。如果失败,那将是非法行为。 唯一的真实原因是十进制大小写的位表示不精确。就像罗勒简单地说的那样,一个例子是最好的解释。只是为了补充他的例子,这是发生了什么:

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)));
}

输出:

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

我们也有:

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)));
}

将输出提供给我们:

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

但是:

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)));
}

具有输出:

BigDec:  10 / 3 = 3.3333 

答案 6 :(得分:-2)

package j2ee.java.math;

/**
 *      Generated from IDL definition of "valuetype "BigDecimal""
 *      TomORB IDL compiler v1.0
 */

public abstract class BigDecimal extends j2ee.java.lang.Number implements org.omg.CORBA.portable.StreamableValue, j2ee.java.lang.Comparable

{

        private String[] _truncatable_ids = {"RMI:java.math.BigDecimal:11F6D308F5398BBD:54C71557F981284F"};



        protected int scale_;

        protected j2ee.java.math.BigInteger intVal;



        /* constants */

        int ROUND_UP = 0;

        int ROUND_DOWN = 1;

        int ROUND_CEILING = 2;

        int ROUND_FLOOR = 3;

        int ROUND_HALF_UP = 4;

        int ROUND_HALF_DOWN = 5;

        int ROUND_HALF_EVEN = 6;

        int ROUND_UNNECESSARY = 7;



        public abstract int _hashCode();



        public abstract int scale();



        public abstract int signum();



        public abstract boolean _equals(org.omg.CORBA.Any arg0);



        public abstract java.lang.String _toString();



        public abstract j2ee.java.math.BigDecimal abs();



        public abstract j2ee.java.math.BigDecimal negate();



        public abstract j2ee.java.math.BigDecimal movePointLeft(int arg0);



        public abstract j2ee.java.math.BigDecimal movePointRight(int arg0);



        public abstract j2ee.java.math.BigDecimal setScale(int arg0);



        public abstract j2ee.java.math.BigDecimal setScale(int arg0, int arg1);



        public abstract j2ee.java.math.BigDecimal valueOf(long arg0);



        public abstract j2ee.java.math.BigDecimal valueOf(long arg0, int arg1);



        public abstract int compareTo(j2ee.java.math.BigDecimal arg0);



        public abstract j2ee.java.math.BigInteger toBigInteger();



        public abstract j2ee.java.math.BigInteger unscaledValue();



        public abstract j2ee.javax.rmi.CORBA.ClassDesc classU0024(java.lang.String arg0);



        public abstract j2ee.java.math.BigDecimal add(j2ee.java.math.BigDecimal arg0);



        public abstract j2ee.java.math.BigDecimal max(j2ee.java.math.BigDecimal arg0);



        public abstract j2ee.java.math.BigDecimal min(j2ee.java.math.BigDecimal arg0);



        public abstract j2ee.java.math.BigDecimal multiply(j2ee.java.math.BigDecimal arg0);



        public abstract j2ee.java.math.BigDecimal subtract(j2ee.java.math.BigDecimal arg0);



        public abstract j2ee.java.math.BigDecimal divide(j2ee.java.math.BigDecimal arg0, int arg1);



        public abstract j2ee.java.math.BigDecimal divide(j2ee.java.math.BigDecimal arg0, int arg1, int arg2);



        public void _write (org.omg.CORBA.portable.OutputStream os)

        {

               super._write( os );

               os.write_long(scale_);

         ((org.omg.CORBA_2_3.portable.OutputStream)os).write_value( new java.lang.String("intVal") );

        }



        public void _read (final org.omg.CORBA.portable.InputStream os)
        {
               super._read( os );

               scale_=os.read_long();

         intVal=(j2ee.java.math.BigInteger)((org.omg.CORBA_2_3.portable.InputStream)os).read_value ( "RMI:java.math.BigInteger:E2F79B6E7A470003:8CFC9F1FA93BFB1D".toString() );
        }

        public String[] _truncatable_ids()
        {
               return _truncatable_ids;
        }

        public org.omg.CORBA.TypeCode _type()
        {
               return j2ee.java.math.BigDecimalHelper.type();
        }
}

答案 7 :(得分:-8)

原始数字类型对于在内存中存储单个值很有用。但是当使用double和float类型处理计算时,舍入存在问题。因为内存表示不能精确映射到值,所以会发生这种情况。例如,double值应该占用64位,但Java不使用所有64位。它只存储它认为数字的重要部分。因此,当您将float或double类型的值一起添加时,可能会出现错误的值。 可能是这段视频https://youtu.be/EXxUSz9x7BM会解释更多