double result = 1.0 - 0.9;
System.out.println(result);//0.09999999999999998
出現這種結果的原因:float和double類型尤其不適合用于貨幣運算,因為要讓一個float或double精確的表示0.1或者任何其他負數次方值是不可能的(十進制系統中不能準確的表示出1/3,同樣二進制系統也不能準確的表示1/10)。
1.十進制整數轉為二進制數:
例子:11表示成二進制數:
11/2 =5 余1
5/2 = 2 余1
2/2 = 1 余0
1/2 = 0 余1
0結束,11二進制表示為(從下往上):1011
注意:只要遇到除以后的結果為0就結束了。所有的整數除以2一定能夠最終得到0,但是小數就不能,小數轉變為二進制的算法就有可能會無限循環下去。
2.十進制小數轉為二進制數
算法是乘以2知道沒有了小數為止,例子:
0.9表示成二進制數:
0.9*2 = 1.8 取整數部分:1
0.8*2 = 1.6 取整數部分:1
0.6*2 = 1.2 取整數:1
0.2*2 = 0.4 取整數:0
0.4*2 = 0.8 取整數:0
0.8*2 = 1.6 取整數:1
。。。。
0.9二進制表示為(從上往下):1100100100100.......
注意:上面的計算過程循環了,也就是說乘以2永遠不能消滅小數部分,這樣算法將無限下去。顯然,小數的二進制表示有時是不能精確的。道理很簡單,十進制系統中不能準確的表示出1/3,同樣二進制也無法準確的表示1/10。這也是浮點型出現精度丟失問題的主要原因。
解決方案一:
如果不介意記錄十進制的小數點,而且數值不大,那么可以使用long,int等基本類型,
int resultInt = 10 -9;
double result ?= (double)resultInt / 100;//自己控制小數點
解決方案二:
使用BigDecimal,而且需要在構造參數使用String類型.
float和double只能用來做科學計算或者工程計算,在商業計算等精確計算中,要用java.math.BigDecimal。
在《EffectiveJava》這本書中就給出了一個解決方法。該書中也指出,float和double只能用來做科學計算或者是工程計算,在商業計算等精確計算中,我們要用java.math.BigDecimal。
BigDecimal類一個有4個方法,我們只關心對我們解決浮點型數據進行精確計算有用的方法,即
BigDecimal(double value) // 將double型數據轉換成BigDecimal型數據
思路很簡單,我們先通過BigDecimal(double
value)方法,將double型數據轉換成BigDecimal數據,然后就可以正常進行精確計算了。等計算完畢后,我們可以對結果做一些處理,比如
對除不盡的結果可以進行四舍五入。最后,再把結果由BigDecimal型數據轉換回double型數據。
這個思路很正確,但是如果你仔細看看API里關于BigDecimal的詳細說明,你就會知道,如果需要精確計算,我們不能直接用double,而非要用
String來構造BigDecimal不可!所以,我們又開始關心BigDecimal類的另一個方法,即能夠幫助我們正確完成精確計算的
BigDecimal(String value)方法。
// BigDecimal(String value)能夠將String型數據轉換成BigDecimal型數據
那么問題來了,想像一下吧,如果我們要做一個浮點型數據的加法運算,需要先將兩個浮點數轉為String型數據,然后用
BigDecimal(String
value)構造成BigDecimal,之后要在其中一個上調用add方法,傳入另一個作為參數,然后把運算的結果(BigDecimal)再轉換為浮
點數。如果每次做浮點型數據的計算都要如此,你能夠忍受這么煩瑣的過程嗎?至少我不能。所以最好的辦法,就是寫一個類,在類中完成這些繁瑣的轉換過程。這
樣,在我們需要進行浮點型數據計算的時候,只要調用這個類就可以了。網上已經有高手為我們提供了一個工具類Arith來完成這些轉換操作。它提供以下靜態
方法,可以完成浮點型數據的加減乘除運算和對其結果進行四舍五入的操作:
public static double add(double v1,double v2)
public static double sub(double v1,double v2)
public static double mul(double v1,double v2)
public static double div(double v1,double v2)
public static double div(double v1,double v2,int scale)
public static double round(double v,int scale)
下面會附上Arith的源代碼,大家只要把它編譯保存好,要進行浮點數計算的時候,在你的源程序中導入Arith類就可以使用以上靜態方法來進行浮點數的精確計算了。
importjava.math.BigDecimal;
/**
*?由于Java的簡單類型不能夠精確的對浮點數進行運算,這個工具類提供精
*?確的浮點數運算,包括加減乘除和四舍五入。
*/
publicclassArith{
//默認除法運算精度
privatestaticfinalintDEF_DIV_SCALE?=10;
//這個類不能實例化
privateArith(){
}
/**
*?提供精確的加法運算。
*?@param?v1?被加數
*?@param?v2?加數
*?@return?兩個參數的和
*/
publicstaticdoubleadd(doublev1,doublev2){
BigDecimal?b1?=newBigDecimal(Double.toString(v1));
BigDecimal?b2?=newBigDecimal(Double.toString(v2));
returnb1.add(b2).doubleValue();
}
/**
*?提供精確的減法運算。
*?@param?v1?被減數
*?@param?v2?減數
*?@return?兩個參數的差
*/
publicstaticdoublesub(doublev1,doublev2){
BigDecimal?b1?=newBigDecimal(Double.toString(v1));
BigDecimal?b2?=newBigDecimal(Double.toString(v2));
returnb1.subtract(b2).doubleValue();
}
/**
*?提供精確的乘法運算。
*?@param?v1?被乘數
*?@param?v2?乘數
*?@return?兩個參數的積
*/
publicstaticdoublemul(doublev1,doublev2){
BigDecimal?b1?=newBigDecimal(Double.toString(v1));
BigDecimal?b2?=newBigDecimal(Double.toString(v2));
returnb1.multiply(b2).doubleValue();
}
/**
*?提供(相對)精確的除法運算,當發生除不盡的情況時,精確到
*?小數點以后10位,以后的數字四舍五入。
*?@param?v1?被除數
*?@param?v2?除數
*?@return?兩個參數的商
*/
publicstaticdoublediv(doublev1,doublev2){
returndiv(v1,v2,DEF_DIV_SCALE);
}
/**
*?提供(相對)精確的除法運算。當發生除不盡的情況時,由scale參數指
*?定精度,以后的數字四舍五入。
*?@param?v1?被除數
*?@param?v2?除數
*?@param?scale?表示表示需要精確到小數點以后幾位。
*?@return?兩個參數的商
*/
publicstaticdoublediv(doublev1,doublev2,intscale){
if(scale<0){
thrownewIllegalArgumentException(
"The?scale?must?be?a?positive?integer?or?zero");
}
BigDecimal?b1?=newBigDecimal(Double.toString(v1));
BigDecimal?b2?=newBigDecimal(Double.toString(v2));
returnb1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
*?提供精確的小數位四舍五入處理。
*?@param?v?需要四舍五入的數字
*?@param?scale?小數點后保留幾位
*?@return?四舍五入后的結果
*/
publicstaticdoubleround(doublev,intscale){
if(scale<0){
thrownewIllegalArgumentException(
"The?scale?must?be?a?positive?integer?or?zero");
}
BigDecimal?b?=newBigDecimal(Double.toString(v));
BigDecimal?one?=newBigDecimal("1");
returnb.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
};
轉自;http://blog.csdn.net/wanted_tao/article/details/52880737