Java 中浮点数---------BigDecimal和double(初探)

为何要使用 bigdecimal?html

借用《Effactive Java》这本书中的话,floatdouble类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供彻底精确的结果,因此不该该被用于要求精确结果的场合。可是,商业计算每每要求结果精确,这时候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

  

分析一下上面代码的问题(注释的内容表示此语句的输出)

第一行:事实上,因为二进制没法精确地表示十进制小数0.1,可是编译器读到字符串"0.1"以后,必须把它转成8个字节的double值,所以,编译器只能用一个最接近的值来代替0.1了,即0.1000000000000000055511151231257827021181583404541015625。所以,在运行时,传给BigDecimal构造函数的真正的数值是0.1000000000000000055511151231257827021181583404541015625。
第二行:BigDecimal可以正确地把字符串转化成真正精确的浮点数。
第三行:问题在于Double.toString会使用必定的精度来四舍五入double,而后再输出。会。Double.toString(0.1000000000000000055511151231257827021181583404541015625)输出的事实上是"0.1",所以生成的BigDecimal表示的数也是0.1。
第四行:基于前面的分析,事实上这一行代码等价于第三行
 
结论:
1.若是你但愿BigDecimal可以精确地表示你但愿的数值,那么必定要使用字符串来表示小数,并传递给BigDecimal的构造函数。
2.若是你使用Double.toString来把double转化字符串,而后调用BigDecimal(String),这个也是不靠谱的,它不必定按你的想法工做。
3.若是你不是很在意是否彻底精确地表示,而且使用了BigDecimal(double),那么要注意double自己的特例,double的规范自己定义了几个特殊的double值(Infinite,-Infinite,NaN),不要把这些值传给BigDecimal,不然会抛出异常。
 
问题二:把double强制转化成int,难道不是扔掉小数部分吗?
int x=(int)1023.99999999999999; // x=1024为何?

缘由仍是在于二进制没法精确地表示某些十进制小数,所以1023.99999999999999在编译以后的double值变成了1024。

因此,把double强制转化成int确实是扔掉小数部分,可是你写在代码中的值,并不必定是编译器生成的真正的double值。
验证代码:
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表示出来还记得吧。

咱们也能够直接打印出d的二进制形式,根据IEEE 754的规定,咱们能够算出0x4090000000000000=(1024)。
 
问题二:BigDecimal如何比较大小?
    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

相关文章
相关标签/搜索