1. 程式人生 > >java中小數的處理:高精度運算用bigDecimal類,精度保留方法,即舍入方式的指定

java中小數的處理:高精度運算用bigDecimal類,精度保留方法,即舍入方式的指定

一、 計算機的小數計算一定範圍內精確,超過範圍只能取近似值:

    計算機儲存的浮點數受儲存bit位數影響,只能保證一定範圍內精準,超過bit範圍的只能取近似值。
    java中各型別的精度範圍參見:http://blog.csdn.net/longshenlmj/article/details/47616481
程式設計時注意:
doulbe型別的數,不能用等號判定是否相等(或者是一定範圍內可以)。因為兩次同樣的計算(除法)結果可能出現小數部分不同。甚至極端的時候,初始化兩個小數時,都可能不相等(用數值和字串分別初始化bigdecimal的小數就會不等)

java小數處理方法的經驗總結:

(1)小數計算對精度無要求時,使用float節省時間。

(2)如果有精度要求,用BigDecimal類處理(初始化必須使用字串,因為用數值初始化會得到近似值,不準確),然後設定保留位數和 舍入法(half_up四捨五入,half_even銀行家,half_down向下取整)

(3)精度要求低時可轉化為整數處理(集體統一擴大數量級):
    乘以10的級數轉化為整數處理,小數點右移幾位,但整數值不要超過對應型別的取值範圍。比如保留4位小數,可統一乘以10000,然後只保留整數計算結果,保留近位的話就多乘一位。
    這種方式在RTB專案MDSP的演算法核心模組中使用,幾十萬的投放量,用int或long就可以處理,更大範圍的整數處理BigInteger。

這樣的好處是:

a,計算快,除了除法,其他運算上整數計算(加減乘)節省時間;
b,除法中,小數部分可直接省略,或向上取整(小數大於0時則加1)也可以讓小數點多移動一位來保留進位。

二、java專門的小數運算類:BigDecimal型別(比double和float小數運算更精準的小數計算)

    float和double只能用來做科學計算或者是工程計算,在廣域數值範圍上提供較為精確的快速近似計算;而在商業計算要求結果精確(比如,有的程式語言中提供了專門的貨幣型別來處理),所以Java使用java.math.BigDecimal專門處理小數精度
構造器描述:
    BigDecimal(int)       建立一個具有引數所指定整數值的物件。 
    BigDecimal(double)    建立一個具有引數所指定雙精度值的物件。 
    BigDecimal(long)      建立一個具有引數所指定長整數值的物件。 
    BigDecimal(String)    建立一個具有引數所指定以字串表示的數值的物件。

使用原則:初始化小數必須用String來夠造,BigDecimal(String),因為用double的小數是近似值,不是精確值。

BigDecimal成員方法
    add(BigDecimal)        物件自身與引數相加,然後返回這個物件。 
    subtract(BigDecimal)   物件自身與引數相減,然後返回這個物件。 
    multiply(BigDecimal)   物件自身與引數相乘,然後返回這個物件。 
    divide(BigDecimal)     物件自身與引數相除,然後返回這個物件。 
    toString()             BigDecimal物件的數值轉換成對應的字串。 
    doubleValue()          BigDecimal對返回double值。 
    floatValue()           BigDecimal對返回float。 
    longValue()            BigDecimal對返回long值。 
    intValue()             BigDecimal對返回int值。

三、java小數保留精度的舍入方式

1,java 常用的四捨五入法實現:

Math類中的round方法不能設定保留幾位小數,但可以乘100達到保留2位的目的:
        Math.round(value*100)/100.0;

或者,直接用java.text.DecimalFormat指定保留幾位小數,用哪幾種舍入法:
        DecimalFormat decFormat = new DecimalFormat("#.00");
        decFormat.setRoundingMode(RoundingMode.HALF_UP);

2,java的8種舍入方式:

1、 ROUND_UP:向上取整(丟掉小數,整數加1) 遠離零方向舍入。向絕對值最大的方向舍入,只要捨棄位非0即進位。

2、ROUND_DOWN:向下取整(丟掉小數)。趨向零方向舍入。向絕對值最小的方向輸入,所有的位都要捨棄,不存在進位情況。

3、ROUND_CEILING:向正無窮方向走,始終不會減少計算值。如果 BigDecimal 為正,則舍入行為與 ROUND_UP 相同;如果為負,則舍入行為與 ROUND_DOWN 相同。Math.round()方法就是使用的此模式。

4、ROUND_FLOOR:向負無窮方向舍入。向負無窮方向靠攏。若是正數,舍入行為類似於ROUND_DOWN;若為負數,舍入行為類似於ROUND_UP。

5、 HALF_UP:四捨五入,最近數字舍入(5進)。

6、 HALF_DOWN:四捨六入,最近數字舍入(5舍)。

7、 HAIL_EVEN:銀行家舍入法。四捨六入五偶舍。即捨棄位4舍6入,當為5時看前一位,奇進偶舍。向“最接近的”數字舍入,如果與兩個相鄰數字的距離相等,則向相鄰的偶數舍入。
     也就是說,如果捨棄部分左邊的數字為奇數,則舍入行為與 ROUND_HALF_UP 相同; 如果為偶數,則舍入行為與 ROUND_HALF_DOWN 相同。
     注意,在重複進行一系列計算時,此舍入模式可以將累加錯誤減到最小。

8、ROUND_UNNECESSARY 斷言請求的操作具有精確的結果,因此不需要舍入。如果對獲得精確結果的操作指定此舍入模式,則丟擲ArithmeticException。

四、BigDecimal的java程式碼例項

package test;import java.math.BigDecimal;publicclassbigDecimalTest{publicstaticvoidmain(String[] args){BigDecimal a =newBigDecimal(1);BigDecimal b =newBigDecimal(3);BigDecimal sum =newBigDecimal("0");for(int i=0;i<2;i++){
            sum = sum.add(a);}//除法運算System.out.println("a/b="+a.divide(b,5,BigDecimal.ROUND_HALF_UP).doubleValue());System.out.println("a/b="+a.divide(b,6,BigDecimal.ROUND_HALF_UP).multiply(newBigDecimal("100")));System.out.println("a/b="+a.divide(b,6,BigDecimal.ROUND_HALF_UP).multiply(newBigDecimal("100")).doubleValue());//      System.out.println("a/b="+a.divide(b,11).doubleValue()); //不指定舍入方式,報錯“Invalid rounding mode”int mytt=100;//整數可以隨意初始化BigDecimal oirginC =newBigDecimal(Integer.toString(mytt));BigDecimal oirgin2 =newBigDecimal(mytt);System.out.println("oirginC:"+oirginC);System.out.println("oirgin2:"+oirgin2);BigDecimal scale =newBigDecimal("0.002");//小數必須用string, 否則會出現誤差,如doubledouble testfloat=0.001;BigDecimal scale2 =newBigDecimal(testfloat);System.out.println(scale);System.out.println(scale2);//輸出為近似數0.001000000000000000020816681711721685132943093776702880859375System.out.println(scale.equals(scale2));//兩個比較物件必須在相同尺度才能用equal,2.0和2.00不相等,所以要用compareSystem.out.println(scale.compareTo(scale2));//輸出中,小於、等於、大於分別對應-1、0、1。可以用與不同尺度scale的物件,只要值相等就行了,如1.0等於1.00if(0== b.compareTo(newBigDecimal("3.00"))){//不同尺度的數System.out.println("equal");}//bigDecimal乘法運算返回int型別System.out.println("multipy:"+oirginC.multiply(scale).intValue());//bigDecimal加法運算返回int型別System.out.println("add:"+b.add(a).doubleValue());//bigDecimal減法運算返回int型別System.out.println("substract:"+b.subtract(newBigDecimal(3.00)).doubleValue());//Math.ceil()向上取整,就是小數不為0則整數加1捨去小數int test1 =3;double test2=0.01;System.out.println(Double.valueOf(Math.ceil(test1 * test2)).intValue());System.out.println(Math.ceil(1.02));}}