Double vs. BigDecimal?

Tru*_* Ha 272 java floating-point double bigdecimal

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

ext*_*eon 408

A BigDecimal是表示数字的精确方式.A Double具有一定的精度.使用各种大小的双精度(比如说d1=1000.0d2=0.001)可能会导致在0.001求和时完全丢弃,因为幅度的差异是如此之大.有了BigDecimal这不会发生.

缺点BigDecimal是它速度较慢,并且以这种方式编程算法(由于+ - */不是过载)更加困难.

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

我建议阅读javadoc,BigDecimal因为他们解释的事情比我在这里做得更好:)

  • 说"BigDecimal是表示数字的确切方式"是误导性的.1/3和1/7不能精确表示在基数为10的数字系统(BigDecimal)或基数为2的数字系统(浮点数或双精度数)中.1/3可以在基数3,基数6,基数9,基数12等中精确表示,并且1/7可以精确地表示在基数7,基数14,基数21等中.BigDecimal的优点是它是任意精度的并且人类习惯于在基数10中得到的舍入误差. (91认同)
  • @Truong Ha:在使用价格时,你想使用BigDecimal.如果您将它们存储在数据库中,您需要类似的东西. (5认同)
  • 是的,我正在计算股票价格,所以我相信 BigDecimal 在这种情况下很有用。 (3认同)
  • 关于它变慢的好一点是,它可以帮助我理解为什么Netflix Ribbon负载平衡器代码处理双精度值,然后有如下代码:`if(Math.abs(loadPerServer-maxLoadPerServer)<0.000001d){` (2认同)

小智 156

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

    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);
Run Code Online (Sandbox Code Playgroud)

节目输出:

0.009999999999999998
0.01
Run Code Online (Sandbox Code Playgroud)

有人还想用双?;)

  • @eldjon这不是真的,看看这个例子:BigDecimal two = new BigDecimal("2"); BigDecimal 8 = new BigDecimal("8"); 的System.out.println(two.divide(8)); 打印出0.25. (7认同)
  • 我无法相信这个答案获得了50多票.初始化一个新的BigDecimal("0.02")意味着你将它的比例设置为2位小数,因此BigDecimal将为你舍入结果.您也可以对双倍进行舍入,您可以轻松获得0.01的预期值. (6认同)
  • 双打前言:D (2认同)
  • @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”)));` (2认同)

小智 47

与double有两个主要区别:

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

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

  • 这个答案真正解释了使用 BigDecimal over double 的区别和原因。性能问题是次要的。 (4认同)

Oli*_*bes 23

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

1/7 = 0.142857142857142857142857142857142857142857...
Run Code Online (Sandbox Code Playgroud)

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

相似的数字1/101/100与小数部分表示为二进制数也具有小数点后的数字的无限数量:

1/10 = binary 0.0001100110011001100110011001100110...
Run Code Online (Sandbox Code Playgroud)

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

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

Java BigDecimal具有额外的优势,即它可以在小数点的两边具有任意(但有限)的数字位数,仅受可用内存的限制.


fis*_*hat 7

BigDecimal是Oracle的任意精度数值库.BigDecimal是Java语言的一部分,适用于从财务到科学的各种应用程序(这就是上下文).

将双打用于某些计算没有任何问题.但是,假设您想要计算Math.Pi*Math.Pi/6,即Riemann Zeta函数的值,实际参数为2(我正在研究的项目).浮点除法会给您带来一个圆角误差的痛苦问题.

另一方面,BigDecimal包含许多用于计算任意精度的表达式的选项.下面的Oracle文档中描述的add,multiply和divide方法在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方法在这些方面很重要.


jfa*_*ior 6

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

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)));
}
Run Code Online (Sandbox Code Playgroud)

输出:

Double:  0,3 - 0,2 = 0.09999999999999998
Float:   0,3 - 0,2 = 0.10000001
BigDec:  0,3 - 0,2 = 0.1
Run Code Online (Sandbox Code Playgroud)

我们还有:

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)));
}
Run Code Online (Sandbox Code Playgroud)

给我们输出:

Double:  10 / 3 = 3.3333333333333335
Float:   10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion
Run Code Online (Sandbox Code Playgroud)

但:

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)));
}
Run Code Online (Sandbox Code Playgroud)

具有输出:

BigDec:  10 / 3 = 3.3333 
Run Code Online (Sandbox Code Playgroud)

  • 该死,你能想象警察在凌晨 2 点破门而入的情景吗?“先生,这是你的代码吗?你知道你用错误的精度来划分这两个数字吗?!现在靠在墙上” (7认同)
  • @Tarek7 这确实是银行、市场、电信等任何与金钱相关的计算的法律问题。如果您看过《超人》,您就会明白,简单的精度改变就可以让您成为百万富翁!:) (3认同)