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;

public class bigDecimalTest {

    public static void main(String[] args) {
        BigDecimal a = new BigDecimal(1);
        BigDecimal b = new BigDecimal(3);

        BigDecimal sum = new BigDecimal("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(new BigDecimal("100")));
        System.out.println("a/b="+a.divide(b,6,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")).doubleValue());
//      System.out.println("a/b="+a.divide(b,11).doubleValue()); //不指定舍入方式,報錯“Invalid rounding mode”

        int mytt= 100;//整數可以隨意初始化
        BigDecimal oirginC = new BigDecimal(Integer.toString(mytt));
        BigDecimal oirgin2 = new BigDecimal(mytt);
        System.out.println("oirginC:"+oirginC);
        System.out.println("oirgin2:"+oirgin2);

        BigDecimal scale = new BigDecimal("0.002");//小數必須用string, 否則會出現誤差,如double
        double testfloat=0.001;
        BigDecimal scale2 = new BigDecimal(testfloat);
        System.out.println(scale);
        System.out.println(scale2);//輸出爲近似數0.001000000000000000020816681711721685132943093776702880859375

        System.out.println(scale.equals(scale2));//兩個比較對象必須在相同尺度才能用equal,2.0和2.00不相等,所以要用compare
        System.out.println(scale.compareTo(scale2));//輸出中,小於、等於、大於分別對應-1、0、1。可以用與不同尺度scale的對象,只要值相等就行了,如1.0等於1.00
        if(0 == b.compareTo(new BigDecimal("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(new BigDecimal(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));


    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章