標籤:
引言
最近在看項目中的各種計算,因為是金融方面的項目,涉及到日化,年化,利息,債轉這些和錢有關的計算很多,其中全部通過BigDecimal類進行的運算,以前涉及到的計算基本都用+、-、*、/等算術運算子直接進行數學運算,資料類型一般用double就能解決問題,但是如果要求完全精度時double就不合適了,因為double是在廣域數值範圍上提供較為精確的快速近似計算(只能處理16位有效數)。
BigDecimal是Java在java.math包中提供的API類,主要用來對超過16位有效位的數進行精確的運算。雙精確度浮點型變數double可以處理16位有效數。在實際應用中,需要對更大或者更小的數進行運算和處理。float和double只能用來做科學計算或者是工程計算,在商業計算中要用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.100000000000000088817841970012523233890533447265625to string: 1.1valueOf: 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,保留模式
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損失a cent,結果也是巨大的。學習亦是如此,每天學習一分,一年後獲得的改變也是巨大的。
Java-BigDecimal 分析