天天看點

BigDecimal介紹

BigDecimal是Java提供的一個不變的、任意精度的有符号十進制數對象。它提供了四個構造器,有兩個是用BigInteger構造,在這裡我們不關心,我們重點看用double和String構造的兩個構造器(有關BigInteger詳細介紹請查閱j2se API文檔)。

BigDecimal(double val)

           Translates a double into a BigDecimal.

BigDecimal(String val)

           Translates the String representation of a BigDecimal into a BigDecimal.

BigDecimal(double)是把一個double類型十進制數構造為一個BigDecimal對象執行個體。

BigDecimal(String)是把一個以String表示的BigDecimal對象構造為BigDecimal對象執行個體。

習慣上,對于浮點數我們都會定義為double或float,但BigDecimal API文檔中對于BigDecimal(double)有這麼一段話:

Note: the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to .10000000000000000555111512312578 27021181583404541015625. This is so because .1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the long value that is being passed in to the constructor is not exactly equal to .1, appearances notwithstanding.

The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal(".1") is exactly equal to .1, as one would expect. Therefore, it is generally recommended that the (String) constructor be used in preference to this one

下面對這段話做簡單解釋:

注意:這個構造器的結果可能會有不可預知的結果。有人可能設想new BigDecimal(.1)等于.1是正确的,但它實際上是等于.1000000000000000055511151231257827021181583404541015625,這就是為什麼.1不能用一個double精确表示的原因,是以,這個被放進構造器中的長值并不精确的等于.1,盡管外觀看起來是相等的。

然而(String)構造器,則完全可預知的,new BigDecimal(“.1”)如同期望的那樣精确的等于.1,是以,(String)構造器是被優先推薦使用的。

看下面的結果:

     System.out.println(new BigDecimal(123456789.02).toString());

      System.out.println(new BigDecimal("123456789.02").toString());

輸出為:

123456789.01999999582767486572265625

123456789.02

現在我們知道,如果需要精确計算,非要用String來夠造BigDecimal不可!

實作方案

現在我們已經知道怎麼解決這個問題了,原則上是使用BigDecimal(String)構造器,我們建議,在商業應用開發中,涉及金額等浮點數計算的資料,全部定義為String,資料庫中可定義為字元型字段,在需要使用這些資料進行運算的時候,使用BigDecimal(String)構造BigDecimal對象進行運算,保證資料的精确計算。同時避免了科學記數法的出現。如果科學記數表示法在應用中不是一種負擔的話,可以考慮定義為浮點類型。

這裡我們提供了一個工具類,定義浮點數的加、減、乘、除和四舍五入等運算方法。以供參考。

源檔案MathExtend.java:

import java.math.BigDecimal;

public class MathExtend

{

//預設除法運算精度

private static final int DEFAULT_DIV_SCALE = 10;

public static double add(double v1, double v2)

{

      BigDecimal b1 = new BigDecimal(Double.toString(v1));

      BigDecimal b2 = new BigDecimal(Double.toString(v2));

      return b1.add(b2).doubleValue();

}

public static String add(String v1, String v2)

{

      BigDecimal b1 = new BigDecimal(v1);

    BigDecimal b2 = new BigDecimal(v2);

      return b1.add(b2).toString();

}

public static double subtract(double v1, double v2)

{

      BigDecimal b1 = new BigDecimal(Double.toString(v1));

      BigDecimal b2 = new BigDecimal(Double.toString(v2));

      return b1.subtract(b2).doubleValue();

}

public static String subtract(String v1, String v2)

{

      BigDecimal b1 = new BigDecimal(v1);

      BigDecimal b2 = new BigDecimal(v2);

      return b1.subtract(b2).toString();

}

public static double multiply(double v1, double v2)

{

      BigDecimal b1 = new BigDecimal(Double.toString(v1));

      BigDecimal b2 = new BigDecimal(Double.toString(v2));

      return b1.multiply(b2).doubleValue();

}