Double与BigDecimal?


Answers:


446

A BigDecimal是表示数字的精确方法。A Double具有一定的精度。由于幅值之差太大,求和时使用各种大小的倍数(例如d1=1000.0d2=0.001)可能会导致一起0.001下降。有了BigDecimal这不会发生。

这样做的缺点BigDecimal是速度较慢,并且以这种方式编程算法会比较困难(由于+ - *并且/没有过载)。

如果您要花钱,或必须精打细算,请使用BigDecimal。否则Doubles往往会足够好。

我确实建议您阅读JavadocBigDecimal因为它们的解释确实比我在这里做的要好:)


是的,我正在计算股票价格,因此我认为BigDecimal在这种情况下很有用。
Truong Ha

5
@Truong Ha:使用价格时,您想使用BigDecimal。如果将它们存储在数据库中,则需要类似的内容。
Extraneon 2010年

98
说“ BigDecimal是表示数字的确切方法”会产生误导。在以10为底的数字系统(BigDecimal)或以2为底的数字系统(浮点数或双精度数)中,不能精确表示1/3和1/7。1/3可以精确地表示为3、6、9、12等,而1/7可以精确地表示为7、14、21等。BigDecimal的优势在于它具有任意精度和人类使用的舍入误差您在基座10获得
procrastinate_later

3
关于它变慢的好一点,可以帮助我理解为什么Netflix Ribbon负载平衡器代码处理双打,然后显示如下代码:if (Math.abs(loadPerServer - maxLoadPerServer) < 0.000001d) {
michaelok

@extraneon我认为您的意思是说“如果必须准确性,请使用BigDecimal”,则Double会具有更多的“精度”(更多数字)。
jspinella

164

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

    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

有人还是想使用double吗?;)


11
@eldjon那不是真的,看这个例子:BigDecimal two = new BigDecimal(“ 2”); BigDecimal八=新的BigDecimal(“ 8”); System.out.println(two.divide(eight)); 打印出0.25。
Ludvig W

4
double forevr:D
vach

但是,如果使用浮点数,则在这种情况下,您可以获得与BigDecimal相同的精度,但性能会更好
EliuX

3
@EliuX Float可能适用于0.03-0.02,但其他值仍不精确:System.out.println(0.003f - 0.002f);BigDecimal准确:System.out.println(new BigDecimal("0.003").subtract(new BigDecimal("0.002")));
Martin

50

与double有两个主要区别:

  • 任意精度,类似于BigInteger,它们可以包含任意精度和大小的数量
  • 以10为底数而不是以2为底数,BigDecimal是n * 10 ^ scale,其中n是任意大的有符号整数,并且可以将scale视为左移或右移小数点的位数

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


2
这个答案确实解释了使用BigDecimal超过double的区别和原因。性能问题是次要的。
Vortex

这不是100%正确。您写道BigDecimal为“ n * 10 ^ scale”。Java仅对负数执行此操作。因此正确的是:“ unscaledValue×10 ^ -scale”。对于正数,BigDecimal由“任意精度整数无标度值和32位整数小数位数”组成,而小数位数是小数点右边的位数。
NOD

25

如果您写下一个小数,1 / 7如十进制值,您将得到

1/7 = 0.142857142857142857142857142857142857142857...

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

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

1/10 = binary 0.0001100110011001100110011001100110...

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

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

Java的BigDecimal另一个优点是,它可以在小数点的两侧具有任意(但有限)个数字,仅受可用内存限制。


7

BigDecimal是Oracle的任意精度数字库。BigDecimal是Java语言的一部分,可用于从财务到科学(即上午)的各种应用程序。

在某些计算中使用双精度数没有错。但是,假设您要计算Math.Pi * Math.Pi / 6,即实际参数为2(我正在研究的项目)的Riemann Zeta函数的值。浮点除法为您带来舍入误差的痛苦问题。

另一方面,BigDecimal包含许多用于以任意精度计算表达式的选项。在BigDecimal Java World中,Oracle文档下面所述的加,乘和除方法“代替” +,*和/:

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

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

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

BigDecimal三分之一=新的BigDecimal(“ 0.33333333333”);

利用1/3的字符串表示形式以指定的精度表示该无限重复的数字。四舍五入错误很可能在JVM内部如此深处,以至于四舍五入错误不会干扰您的大多数实际计算。从我个人的经验来看,我发现四舍五入是逐渐增加的。从Oracle文档中可以看出,setScale方法在这些方面很重要。


BigDecimal为一部分Java的任意精度的数字图书馆。在这种情况下,“内部”是毫无意义的,尤其是它是由IBM编写的。
罗恩侯爵,

@EJP:我研究了BigDecimal类,并了解到其中只有一部分是由IBM编写的。版权评论如下: /* * Portions Copyright IBM Corporation, 2001. All Rights Reserved. */
realPK '17

7

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

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 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.