Java BigDecimal详解

来源:互联网 发布:新手如何开淘宝网店 编辑:程序博客网 时间:2024/05/09 18:51

原文地址:http://blog.csdn.net/jackiehff/article/details/8582449#comments

1.引言

        借用《Effactive Java》这本书中的话,float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。但是,商业计算往往要求结果精确,这时候BigDecimal就派上大用场啦。

 

2.BigDecimal简介

        BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负scale 次幂。因此,BigDecimal表示的数值是(unscaledValue × 10-scale)。

 

3.测试代码

3.1构造函数(主要测试参数类型为double和String的两个常用构造函数)

       BigDecimal aDouble =new BigDecimal(1.22);       System.out.println("construct with a double value: " + aDouble);       BigDecimal aString = new BigDecimal("1.22");       System.out.println("construct with a String value: " + aString);       你认为输出结果会是什么呢?如果你没有认为第一个会输出1.22,那么恭喜你答对了,输出结果如下:       construct with a doublevalue:1.2199999999999999733546474089962430298328399658203125       construct with a String value: 1.22 

       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)方法。

3.2 加法操作

        BigDecimal a =new BigDecimal("1.22");        System.out.println("construct with a String value: " + a);        BigDecimal b =new BigDecimal("2.22");        a.add(b);        System.out.println("aplus b is : " + a);        我们很容易会认为会输出:        construct with a Stringvalue: 1.22        a plus b is :3.44        但实际上a plus b is : 1.22

4.源码分析

4.1 valueOf(double val)方法

    public   static BigDecimal valueOf(double val) {       // Reminder: a zero double returns '0.0', so we cannotfastpath       // to use the constant ZERO. This might be important enough to       // justify a factory approach, a cache, or a few private       // constants, later.       return new BigDecimal(Double.toString(val));//见3.1关于JDK描述的第三点    }

4.2 add(BigDecimal augend)方法

      public BigDecimal add(BigDecimal augend) {          long xs =this.intCompact; //整型数字表示的BigDecimal,例a的intCompact值为122          long ys = augend.intCompact;//同上          BigInteger fst = (this.intCompact !=INFLATED) ?null :this.intVal;//初始化BigInteger的值,intVal为BigDecimal的一个                                                                                        BigInteger 类型的属性          BigInteger snd =(augend.intCompact !=INFLATED) ?null : augend.intVal;          int rscale =this.scale;//小数位数           long sdiff = (long)rscale - augend.scale;//小数位数之差          if (sdiff != 0) {//取小数位数多的为结果的小数位数              if (sdiff < 0) {                 int raise =checkScale(-sdiff);                 rscale =augend.scale;                 if (xs ==INFLATED ||                     (xs = longMultiplyPowerTen(xs,raise)) ==INFLATED)                     fst =bigMultiplyPowerTen(raise);                }else {                   int raise =augend.checkScale(sdiff);                   if (ys ==INFLATED ||(ys =longMultiplyPowerTen(ys,raise)) ==INFLATED)                       snd = augend.bigMultiplyPowerTen(raise);               }          }          if (xs !=INFLATED && ys !=INFLATED) {              long sum = xs + ys;              if ( (((sum ^ xs) &(sum ^ ys))) >= 0L)//判断有无溢出                 return BigDecimal.valueOf(sum,rscale);//返回使用BigDecimal的静态工厂方法得到的BigDecimal实例           }           if (fst ==null)               fst =BigInteger.valueOf(xs);//BigInteger的静态工厂方法           if (snd ==null)               snd =BigInteger.valueOf(ys);           BigInteger sum =fst.add(snd);           return (fst.signum == snd.signum) ?new BigDecimal(sum,INFLATED, rscale, 0) :              new BigDecimal(sum,compactValFor(sum),rscale, 0);//返回通过其他构造方法得到的BigDecimal对象       }

        以上只是对加法源码的分析,减乘除其实最终都返回的是一个新的BigDecimal对象,因为BigInteger与BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以a.add(b);虽然做了加法操作,但是a并没有保存加操作后的值,正确的用法应该是a=a.add(b);

 

5.总结

        (1)商业计算使用BigDecimal。

        (2)尽量使用参数类型为String的构造函数。

        (3) BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以在做加减乘除运算时千万要保存操作后的值。

        (4)我们往往容易忽略JDK底层的一些实现细节,导致出现错误,需要多加注意。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 血糖高的孕妇便秘怎么办 血糖低怎么办吃什么好 孕检空腹血糖高怎么办 怀孕4个月血糖高怎么办 怀孕3个月血糖高怎么办 怀孕6个月血糖高怎么办 孕29周血糖高怎么办 餐后血糖偶尔高怎么办 歺后血糖9.8高怎么办 怀孕5个月血糖高怎么办 歺后2小时血糖高怎么办 怀孕餐后血糖高怎么办 老人餐后血糖高怎么办 血糖高尿糖不高怎么办 小孩鼻子不通气怎么办特效方法 婴儿20天不大便怎么办 新生儿8天没大便怎么办 新生儿2天没大便怎么办 新生儿4天没大便怎么办 2岁宝宝便秘严重怎么办 婴儿7天没有大便怎么办 新生儿6天没大便怎么办 大便带鲜血 不疼怎么办 宝宝发烧到39度怎么办 肛裂大便出鲜血怎么办 拉大便有血怎么办啊 长痔疮拉大便血怎么办 病人卧床太久不解大便怎么办 宝宝拉泡沫便便怎么办 孩子拉肚子怎么办呢有好办法吗 2岁宝宝大便带血怎么办 十个月宝宝肛裂怎么办 大便时拉出血怎么办啊 2岁宝宝大便干燥怎么办 5岁儿童大便干燥怎么办 九个月宝宝平血怎么办 6个月宝宝肛裂怎么办 3岁儿童大便干燥怎么办 外痔疮出血了该怎么办 外痔疮流血了怎么办呢 外痔疮破了出血怎么办