在《你可能不知道的Java基礎知識(一)》中,我提到使用浮點運算要慎重,感受說的不夠透徹,其實float和double類型主要是爲科學和工程計算而設計的。他們執行的是二進制浮點運算,因爲二進制的侷限性,有時候沒法獲得準確的結果。java
例如:System.out.println(2.0-1.1)將輸出0.8999999999999999,而不是0.9,固然這在科學計算中可有可無,經過四捨五入就能夠輕鬆解決問題,可是在禁止出現舍入偏差的運算中(好比金融計算)就不適用了。ide
在二進制中沒法精確地表示10的任何負數次方值,好比0.1,這和十進制中沒法精確表示1/3一個道理,因此越到像金融貨幣計算的問題,咱們不得不捨棄float和double,而改BigDecimal類。spa
在《Effective Java》這本書中也提到這個原 則,float和double只能用來作科學計算或者是工程計算,在商業計算中咱們要用java.math.BigDecimal,並且非要用 String來夠造BigDecimal不可!在《Effective Java》一書中的例子是用String來夠造BigDecimal的,可是書上 卻沒有強調這一點,這也許是一個小小的失誤吧。設計
用BigDecimal解決上述2.0-1.1問題的代碼爲:code
import java.math.*; class BigDecimalTest { public static void main(String[] args) { //使用字符串構造BigDecimal對象 BigDecimal a =new BigDecimal("2.0"); BigDecimal b = new BigDecimal("1.1"); System.out.println(a.subtract(b)); } } 輸出:0.9
假如不用String來構造BigDecimal問題將依舊:對象
import java.math.*; class TestB { public static void main(String[] args) { //使用double來構造BigDecimal,問題依舊 BigDecimal a = new BigDecimal(2.0); BigDecimal b = new BigDecimal(1.1); System.out.println(a.subtract(b)); } } 輸出 0.899999999999999911182158029987476766109466552734375
固然BigDecimal也提供了舍入方法,下面是網上淘來的類,完美的解決了這些問題:ip
package com.morningstar.bigdecimal; import java.math.BigDecimal; public class Arithmetic4Double { //默認除法運算精度 private static final int DEF_DIV_SCALE = 10; //全部方法均用靜態方法實現,不容許實例化 private Arithmetic4Double() {} /** * 實現浮點數的加法運算功能 * @param v1 加數1 * @param v2 加數2 * @return v1+v2的和 */ 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(); } /** * 實現浮點數的減法運算功能 * @param v1 被減數 * @param v2 減數 * @return v1-v2的差 */ public static double sub(double v1,double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.subtract(b2).doubleValue(); } /** * 實現浮點數的乘法運算功能 * @param v1 被乘數 * @param v2 乘數 * @return v1×v2的積 */ public static double multi(double v1,double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.multiply(b2).doubleValue(); } /** * 實現浮點數的除法運算功能 * 當發生除不盡的狀況時,精確到小數點之後DEF_DIV_SCALE位(默認爲10位),後面的位數進行四捨五入。 * @param v1 被除數 * @param v2 除數 * @return v1/v2的商 */ public static double div(double v1,double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.divide(b2,DEF_DIV_SCALE,BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 實現浮點數的除法運算功能 * 當發生除不盡的狀況時,精確到小數點之後scale位,後面的位數進行四捨五入。 * @param v1 被除數 * @param v2 除數 * @param scale 表示須要精確到小數點之後幾位 * @return v1/v2的商 */ public static double div(double v1,double v2,int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 提供精確的小數位四捨五入功能 * @param v 須要四捨五入的數字 * @param scale 小數點後保留幾位 * @return 四捨五入後的結果 */ public static double round(double v,int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b = new BigDecimal(Double.toString(v)); BigDecimal one = new BigDecimal("1"); return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); } }
總結一句話:精確運算要用String構造的BigDecimal,不要用float和double!ci