我必须计算一些浮点变量,我的同事建议我使用BigDecimal而不是double,因为它会更精确。但我想知道它是什么,以及如何最大限度地利用BigDecimal?
BigDecimal
double
BigDecimal是一种精确表示数字的方法。A Double具有一定的精度。处理不同大小的双精度数(例如d1=1000.0和d2=0.001)可能会导致在求和时由于大小差异太大而将0.001一起删除。而BigDecimal则不会发生这种情况。
Double
d1=1000.0
d2=0.001
0.001
BigDecimal的缺点是它更慢,并且以这种方式编程算法有点困难(由于+ - *和/没有被重载)。
+
-
*
/
如果你处理的是金钱,或者精度是必须的,使用BigDecimal。否则Doubles就足够了。
Doubles
我建议你阅读BigDecimal的javadoc,因为它们确实比我在这里解释得更好:)
它与double有两个主要区别:
n*10^-scale
说BigDecimal可以表示任何数字仍然是不正确的。但是你应该使用BigDecimal进行货币计算的两个原因是:
BigDecimal是Oracle的任意精度数值库。BigDecimal是Java语言的一部分,对于从金融到科学的各种应用程序都很有用。
在某些计算中使用double并没有错。但是,假设您想计算数学。π *数学。Pi / 6,也就是黎曼ζ函数实参为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方法在这些方面很重要。
我的英语不好,所以我在这里只写一个简单的例子。
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吗?;)
如果你写下一个像1 / 7这样的小数值,你会得到
1 / 7
1/7 = 0.142857142857142857142857142857142857142857...
无限重复的数字142857。由于您只能写入有限数量的数字,因此不可避免地会引入舍入(或截断)错误。
142857
像1/10或1/100这样的数字表示为带小数部分的二进制数,小数点后也有无限位数:
1/10
1/100
1/10 = binary 0.0001100110011001100110011001100110...
Doubles将值存储为二进制,因此仅通过将十进制数转换为二进制数可能会引入错误,甚至不做任何算术。
另一方面,十进制数字(如BigDecimal)按原样存储每个十进制数字(二进制编码,但每个小数单独存储)。这意味着在一般意义上,十进制类型并不比二进制浮点或定点类型更精确(即它不能在不损失精度的情况下存储1/7),但它对于具有有限位数的十进制数字更准确,就像货币计算通常的情况一样。
1/7
Java的BigDecimal还有一个额外的优点,它可以在小数点的两边有任意(但有限)位数,只受可用内存的限制。
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
如果你需要在算术中使用除法,你需要使用双而不是BigDecimal。BigDecimal中的除法(divide(BigDecimal) method)是非常无用的,因为BigDecimal不能处理重复的十进制有理数(除法在除数处,会抛出java.lang.ArithmeticException: Non-terminating decimal expansion;没有精确可表示的十进制结果。
(divide(BigDecimal) method)
java.lang.ArithmeticException: Non-terminating decimal expansion;
试试BigDecimal.ONE.divide(new BigDecimal("3"));吧
BigDecimal.ONE.divide(new BigDecimal("3"));
另一方面,Double可以很好地处理除法(可以理解的精度大约是15位有效数字)