java.math.BigDecimal記錄

1.引言 借用《Effactive Java》這本書中的話,float和double類型的主要設計目標是爲了科學計算和工程計算。他們執行二進制浮點運算,這是爲了在廣域數值範圍上提供較爲精確的快速近似計算而精心設計的。然而,它們沒有提供徹底精確的結果,因此不該該被用於要求精確結果的場合。可是,商業計算每每要求結果精確,這時候BigDecimal就派上大用場啦。java

2.BigDecimal簡介 BigDecimal 由任意精度的整數非標度值 和32 位的整數標度 (scale) 組成。若是爲零或正數,則標度是小數點後的位數。若是爲負數,則將該數的非標度值乘以 10 的負scale 次冪。所以,BigDecimal表示的數值是(unscaledValue × 10-scale)。git

3.測試代碼app

3.1構造函數(主要測試參數類型爲double和String的兩個經常使用構造函數)ide

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(doubleval)方法
    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.
       returnnew 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);this

5.總結設計

** (1)商業計算使用BigDecimal。
(2)儘可能使用參數類型爲String的構造函數。
(3) BigDecimal都是不可變的(immutable)的,在進行每一步運算時,都會產生一個新的對象,因此在作加減乘除運算時千萬要保存操做後的值。
(4)咱們每每容易忽略JDK底層的一些實現細節,致使出現錯誤,須要多加註意。**

BigDecimal 由任意精度的整數非標度值 和 32 位的整數標度 (scale) 組成。若是爲零或正數,則標度是小數點後的位數。若是爲負數,則將該數的非標度值乘以 10 的負 scale 次冪。所以,BigDecimal 表示的數值是 (unscaledValue × 10-scale)。

能夠處理任意長度的浮點數運算。

BigDecimal add(BigDecimal val) //BigDecimal 加法

BigDecimal subtract (BigDecimal val) //BigDecimal 減法

BigDecimal multiply (BigDecimal val) //BigDecimal 乘法

BigDecimal divide (BigDecimal val,RoundingMode mode) 除法

具體使用 計算:      

加: a.add(b);
減: a.subtract(b);
乘: a.multiply(b);
除: a.divide(b,2);//2爲精度取值
** 除法細解:
//注意如下相除會拋出異常,緣由: 經過BigDecimal的divide方法進行除法時當不整除,出現無限循環小數時,就會拋異常
//BigDecimal divideBg = a.divide(b);
//解決方法是:設置精確度;就是給divide設置精確的小數點
divide(xxxxx,2, BigDecimal.ROUND_HALF_EVEN)
//其中的第二個參數表示的是:保留小數點以後多少位 **

BigDecimal不整除拋出的異常,請設置精確度! 
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result. 
at java.math.BigDecimal.divide(BigDecimal.java:1278) 
at main.Main.main(Main.java:41)

下面咱們來看看除法的詳細說明: divide(BigDecimal divisor, int scale, introundingMode)

BigDecimal的setScale方法

BigDecimal.setScale()

方法用於格式化小數點

表示保留一位小數,默認用四捨五入方式 setScale(1)

直接刪除多餘的小數位,如2.35會變成2.3 setScale(1,BigDecimal.ROUND_DOWN)

進位處理,2.35變成2.4 setScale(1,BigDecimal.ROUND_UP)

四捨五入,2.35變成2.4 setScale(1,BigDecimal.ROUND_HALF_UP)

四捨五入,2.35變成2.3,若是是5則向下舍setScaler(1,BigDecimal.ROUND_HALF_DOWN)

**注意點一 **

scale指的是你小數點後的位數。 scale()就是BigDecimal類中的方法。如

BigDecimal b = new BigDecimal("123.456");

b.scale()返回的就是3

注意點二
roundingMode是小數的保留模式。它們都是BigDecimal中的常量字段,

有不少種,如 BigDecimal.ROUND_HALF_UP表示的就是4舍5入

**注意點三 **

divide(BigDecimal divisor, int scale, introundingMode)的意思是說: 我用一個BigDecimal對象除以divisor後的結果,而且要求這個結果保留有scale個小數位,roundingMode表示的就是保留模式是什麼,是四捨五入啊仍是其它的

BigDecimal aa = new BigDecimal(135.95 );

BigDecimal bb=new BigDecimal("100" );

BigDecimal result=aa.multiply(bb); //作加法

**3.java中 BigDecimal類型的能夠轉換到double類型: **

用 變量.doubleValue();函數 便可將 BigDecimal 類型數據 轉化爲 double類型!

**4.java BigDecimal比較大小 **

能夠經過BigDecimal的compareTo方法來進行比較。 返回的結果是int類型,-1表示小於,0是等於,1是大於。

看下面這個例子: BigDecimal a = new BigDecimal("1.00"); BigDecmial b = new BigDecimal(1);

想比較一下a和b的大小,通常都會用equals

System.out.println(a.equals(b)); 可是輸出結果是:false 緣由是:BigDecimal比較時,不只比較值,並且還比較精度?

if(a.compareTo(b)==0) 結果是true

比較大小能夠用 a.compareTo(b) 返回值 -1 小於 0 等於 1 大於

**5.BigDecimal取其中最大、最小值、絕對值、相反數: **

  a.max (b) //比較取最大值

  a.min(b) //比較取最小值

  a.abs()//取最絕對值

  a.negate()//取相反數

6.下面是注意 :

BigDecimal枚舉常量用法摘要 :

CEILING 向正無限大方向舍入的舍入模式。
DOWN 向零方向舍入的舍入模式。
FLOOR 向負無限大方向舍入的舍入模式。
HALF_DOWN 向最接近數字方向舍入的舍入模式,若是與兩個相鄰數字的距離相等,則向下舍入。
HALF_EVEN 向最接近數字方向舍入的舍入模式,若是與兩個相鄰數字的距離相等,則向相鄰的偶數舍入。
HALF_UP 向最接近數字方向舍入的舍入模式,若是與兩個相鄰數字的距離相等,則向上舍入。
UNNECESSARY 用於斷言請求的操做具備精確結果的舍入模式,所以不須要舍入。
UP 遠離零方向舍入的舍入模式。

**7.關於BigDecimal格式化 **

public String formatValue(Object value){ 
        String content = null; 
        if (value == null) { 
             content = ""; 
         } else { 
             if(value instanceof BigDecimal){ 
                 //conver to fortmat String 
                 NumberFormat nf = NumberFormat.getInstance(); 
                 nf.setMinimumFractionDigits(2); 
                 nf.setMaximumFractionDigits(2); 
                 content = nf.format(value);  
             }else{ 
                 content = String.valueOf(value); 
             } 
         } 
        return content; 
    }

使用這樣一個方法能夠達到格式化的效果,其中value instanceof BigDecimal,表示的是字符類型是BigDecimal類型的時候執行,這裏的NumberFormat就表示字符類型,下面的兩句代碼就表示小數點後面的精確位數。

這裏還要提到NumberFormat的其餘兩個類型:

getCurrencyInstance(): 返回當前默認 環境的貨幣格式

CurrencyInstance(): 返回指定語言 環境的數字格式,通常是百分比格式

**8.在《Effective Java》這本書中也提到這個原則,float和double只能用來作科學計算或者是工程計算,在商業計算中咱們要用java.math.BigDecimal。 咱們若是須要精確計算,非要用String來夠造BigDecimal不可! **

下面的這個工具類是是轉載別人的,能夠經過個工具類實現小數的精確計算。

1import java.math.BigDecimal;   
2/** *//**  
3* 因爲Java的簡單類型不可以精確的對浮點數進行運算,這個工具類提供精  
4* 確的浮點數運算,包括加減乘除和四捨五入。  
5*/  
6public class Arith{   
7    //默認除法運算精度   
8    private static final int DEF_DIV_SCALE = 10;   
9    //這個類不能實例化   
10    private Arith(){   
11    }   
12  
13    /** *//**  
14     * 提供精確的加法運算。  
15     * @param v1 被加數  
16     * @param v2 加數  
17     * @return 兩個參數的和  
18     */  
19    public static double add(double v1,double v2){   
20        BigDecimal b1 = new BigDecimal(Double.toString(v1));   
21        BigDecimal b2 = new BigDecimal(Double.toString(v2));   
22        return b1.add(b2).doubleValue();   
23    }   
24    /** *//**  
25     * 提供精確的減法運算。  
26     * @param v1 被減數  
27     * @param v2 減數  
28     * @return 兩個參數的差  
29     */  
30    public static double sub(double v1,double v2){   
31        BigDecimal b1 = new BigDecimal(Double.toString(v1));   
32        BigDecimal b2 = new BigDecimal(Double.toString(v2));   
33        return b1.subtract(b2).doubleValue();   
34    }    
35    /** *//**  
36     * 提供精確的乘法運算。  
37     * @param v1 被乘數  
38     * @param v2 乘數  
39     * @return 兩個參數的積  
40     */  
41    public static double mul(double v1,double v2){   
42        BigDecimal b1 = new BigDecimal(Double.toString(v1));   
43        BigDecimal b2 = new BigDecimal(Double.toString(v2));   
44        return b1.multiply(b2).doubleValue();   
45    }   
46  
47    /** *//**  
48     * 提供(相對)精確的除法運算,當發生除不盡的狀況時,精確到  
49     * 小數點之後10位,之後的數字四捨五入。  
50     * @param v1 被除數  
51     * @param v2 除數  
52     * @return 兩個參數的商  
53     */  
54    public static double div(double v1,double v2){   
55        return div(v1,v2,DEF_DIV_SCALE);   
56    }   
57  
58    /** *//**  
59     * 提供(相對)精確的除法運算。當發生除不盡的狀況時,由scale參數指  
60     * 定精度,之後的數字四捨五入。  
61     * @param v1 被除數  
62     * @param v2 除數  
63     * @param scale 表示表示須要精確到小數點之後幾位。  
64     * @return 兩個參數的商  
65     */  
66    public static double div(double v1,double v2,int scale){   
67        if(scale<0){   
68            throw new IllegalArgumentException(   
69                "The scale must be a positive integer or zero");   
70        }   
71        BigDecimal b1 = new BigDecimal(Double.toString(v1));   
72        BigDecimal b2 = new BigDecimal(Double.toString(v2));   
73        return b1.divid(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();   
74    }   
75  
76    /** *//**  
77     * 提供精確的小數位四捨五入處理。  
78     * @param v 須要四捨五入的數字  
79     * @param scale 小數點後保留幾位  
80     * @return 四捨五入後的結果  
81     */  
82    public static double round(double v,int scale){   
83        if(scale<0){   
84            throw new IllegalArgumentException(   
85                "The scale must be a positive integer or zero");   
86        }   
87        BigDecimal b = new BigDecimal(Double.toString(v));   
88        BigDecimal one = new BigDecimal("1");   
89        return b.divid(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();   
90     }   
91  }

原文地址:http://zhangyinhu8680.iteye.com/blog/1536397

相關文章
相關標籤/搜索