Java-BigDecimal 分析

引言

  最近在看項目中的各種計算,因爲是金融方面的項目,涉及到日化,年化,利息,債轉這些和錢有關的計算很多,其中全部通過BigDecimal類進行的運算,以前涉及到的計算基本都用+、-、*、/等算術運算符直接進行數學運算,數據類型一般用double就能解決問題,但是如果要求完全精度時double就不合適了,因爲double是在廣域數值範圍上提供較爲精確的快速近似計算(只能處理16位有效數)。

  BigDecimalJavajava.math包中提供的API類,主要用來對超過16位有效位的數進行精確的運算。雙精度浮點型變量double可以處理16位有效數。在實際應用中,需要對更大或者更小的數進行運算和處理。floatdouble只能用來做科學計算或者是工程計算,在商業計算中要java.math.BigDecimal

  BigDecimal所創建的是對象,我們不能使用傳統的+-*/等算術運算符直接對其對象進行數學運算,而必須調用其相對應的方法。方法中的參數也必須是BigDecimal的對象。下面來看BigDecimal的構造方法。

 

構造方法

  BigDecimal(int)創建一個具有參數所指定整數值的對象。

  BigDecimal(double)創建一個具有參數所指定雙精度值的對象。

  BigDecimal(long)創建一個具有參數所指定長整數值的對象。

  BigDecimal(String)創建一個具有參數所指定以字符串表示的數值的對象。

 

 這裏需要注意的是double類型的,先看一個實例:

<pre name="code" class="java">//1、BigDecimal(double)
BigDecimal aDouble =new BigDecimal(1.1);
System.out.println("to double: " + aDouble);

 // 2、BigDecimal(String)
BigDecimal aString = new BigDecimal("1.1");
System.out.println("to string: " + aString);

//3、BigDecimal.valueOf(double)
BigDecimal aValue = BigDecimal.valueOf(1.1);
System.out.println("valueOf: " + aValue);


 輸出結果:

to double: 1.100000000000000088817841970012523233890533447265625
to string: 1.1
valueOf: 1.1
 

 1、這裏我們注意到參數爲double類型是,構造的BigDecimal值實際上等於1.100000000000000088817841970012523233890533447265625,而不是1.1,這是因爲1.1無法準確地表示爲double(或者說對於該情況,不能表示爲任何有限長度的二進制小數)。

 2、參數爲String類型構造的BigDecimal值正好等於原值1.1。因此,在我們構造BigDecimal值時通常選擇參數爲String類型的構造方法。

 3、當double 必須用作BigDecimal的參數時,可以先使用Double.toString(double)方法將double轉換爲String,然後使用BigDecimal(String)構造方法。再使用BigDecimal.valueOf(double)方法獲取值。

 

運算方式

  加減乘除最基本的運算

  BigDecimal add(BigDecimal augend) 加法運算

  BigDecimal subtract(BigDecimal subtrahend) 減法運算

  BigDecimal multiply(BigDecimal multiplicand) 乘法運算

  BigDecimal divide(BigDecimal divisor) 除法運算


  因爲追求高精度,我們使用BigDecimal,但是在進行帶除法的運算時,並且結果需要保留有效位數的時,可能會出現精度問題。那什麼時候會出現呢?

  我們知道10除3是永遠除不盡的,並且結果我們需要進行格式化(保留有效位數)

 

精度和保留模式

  BigDecimal對象的精度沒有限制。對於除不盡的除法運算,比如10/3,divide方法將會拋出 java.lang.ArithmeticException錯誤,所以除法運算要儘量使用divide(BigDecimal d, int scale, int roundMode)指定標度和保留模式來避免異常。

  scale,標度,小數點保留的位數

  roundMode,保留模式

    roundMode也可以用以下枚舉的方式:

    BigDecimal.ROUND_DOWN:直接丟掉標度以外的小數

    BigDecimal.ROUND_UP:不管捨棄的小數是幾,都進1

    BigDecimal.ROUND_HALF_UP:最常見的四捨五入

    還有其他的模式,可以直接去源碼裏看。


來看實例計算:

<pre name="code" class="java">BigDecimal a =new BigDecimal("10");
BigDecimal c = a.divide(new BigDecimal(3),10,BigDecimal.ROUND_DOWN);
BigDecimal d = c.multiply(new BigDecimal(3));
System.out.println("value: " + c);
System.out.println("value: " + d);


輸出結果爲:

value: 3.3333333333
value: 9.9999999999

因爲第一步的除法指定了標度和保留模式,我們得到的是一個近似值,已經丟失了精度,在進行後續的運算中,精度已經無法保證,如何解決這種問題?應該把除法運算放在整個運算的最後,以減小除法帶來的精度問題。


總結

  商業計算對錢最敏感,一分一毫平時看來很小,但是放在million,billion中,everyhundred損失acent,結果也是巨大的。學習亦是如此,每天學習一分,一年後獲得的改變也是巨大的。




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