为何要使用 bigdecimal?html
借用《Effactive Java》这本书中的话,float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供彻底精确的结果,因此不该该被用于要求精确结果的场合。可是,商业计算每每要求结果精确,这时候BigDecimal就派上大用场啦。java
BigDecimal简介函数
BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组成。若是为零或正数,则标度是小数点后的位数。若是为负数,则将该数的非标度值乘以 10 的负scale 次幂。所以,BigDecimal表示的数值是(unscaledValue × 10-scale)。源码分析
构造方法spa
BigDecimal aString = new BigDecimal("1.22");设计
BigDecimal aDouble = new BigDecimal(1.22);code
BigDecimal bDouble = BigDecimal.valueOf(1.22);orm
System.out.println(aString);htm
System.out.println(aDouble);对象
System.out.println(bDouble);
Result:
1.22
1.2199999999999999733546474089962430298328399658203125
1.22
源码分析:
BigDecimal.valueOf(1.22) 构造方法,实际上是调用 构造方法BigDecimal(String val);
Jdk中有说明:
1、参数类型为double的构造方法的结果有必定的不可预知性。有人可能认为在Java中写入newBigDecimal(0.1)所建立的BigDecimal正好等于 0.1(非标度值 1,其标度为 1),可是它实际上等于0.1000000000000000055511151231257827021181583404541015625。这是由于0.1没法准确地表示为 double(或者说对于该状况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。
2、另外一方面,String 构造方法是彻底可预知的:写入 newBigDecimal("0.1") 将建立一个 BigDecimal,它正好等于预期的 0.1。所以,比较而言,一般建议优先使用String构造方法。
3、当double必须用做BigDecimal的源时,请注意,此构造方法提供了一个准确转换;它不提供与如下操做相同的结果:先使用Double.toString(double)方法,而后使用BigDecimal(String)构造方法,将double转换为String。要获取该结果,请使用static valueOf(double)方法。
在数据量精度不是很大的状况下,经过decimal输出正确的结果。(保留三位小数)
BigDecimal aString = new BigDecimal("1.22");
BigDecimal aDouble = new BigDecimal(1.22);
BigDecimal aValue = BigDecimal.valueOf(1.22);
System.out.println(new DecimalFormat("0.000").format(aString));
System.out.println(new DecimalFormat("0.000").format(aDouble));
System.out.println(new DecimalFormat("0.000").format(aValue));
Result:
1.220
1.220
1.220
缘由分析:DecimalFormat作用了本身的舍余规则
总结
(1)商业计算使用BigDecimal。
(2)尽可能使用参数类型为String的构造函数。
(3) BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,因此在作加减乘除运算时千万要保存操做后的值。
(4)咱们每每容易忽略JDK底层的一些实现细节,致使出现错误,须要多加注意。
问题一:BigDecimal的精度问题(StackOverflow上有个家伙问了相关的问题)
System.out.println(new BigDecimal(0.1).toString()); // 0.1000000000000000055511151231257827021181583404541015625 System.out.println(new BigDecimal("0.1").toString()); // 0.1 System.out.println(new BigDecimal( Double.toString(0.1000000000000000055511151231257827021181583404541015625)).toString());// 0.1 System.out.println(new BigDecimal(Double.toString(0.1)).toString()); // 0.1
分析一下上面代码的问题(注释的内容表示此语句的输出)
int x=(int)1023.99999999999999; // x=1024为何?
缘由仍是在于二进制没法精确地表示某些十进制小数,所以1023.99999999999999在编译以后的double值变成了1024。
double d = 1023.99999999999999; int x = (int) d; System.out.println(new BigDecimal(d).toString()); // 1024 System.out.println(Long.toHexString(Double.doubleToRawLongBits(d))); // 4090000000000000 System.out.println(x); // 1024
前面提过BigDecimal能够精确地把double表示出来还记得吧。
public static void main(String[] args) { BigDecimal a = BigDecimal.valueOf(1.01); BigDecimal b = BigDecimal.valueOf(1.00000001); if (a.compareTo(b) == 0) { System.out.println("a==b"); } else if (a.compareTo(b) == 1) { System.out.println("a>b"); } else if (a.compareTo(b) == -1) { System.out.println("a<b"); } }
参考:
http://www.cnblogs.com/mingforyou/p/3344489.html
http://stackoverflow.com/questions/8073912/why-do-we-need-to-convert-the-double-into-a-string-before-we-can-convert-it-int
http://www.cnblogs.com/linjiqin/p/3413894.html