關于float和double丟失精度問題及解決方案

double result = 1.0 - 0.9;

System.out.println(result);//0.09999999999999998

出現(xiàn)這種結果的原因:float和double類型尤其不適合用于貨幣運算鲫骗,因為要讓一個float或double精確的表示0.1或者任何其他負數(shù)次方值是不可能的(十進制系統(tǒng)中不能準確的表示出1/3觉义,同樣二進制系統(tǒng)也不能準確的表示1/10)怖喻。

1.十進制整數(shù)轉為二進制數(shù):

例子:11表示成二進制數(shù):

11/2 =5 余1

5/2 = 2 余1

2/2 = 1 余0

1/2 = 0 余1

0結束媒至,11二進制表示為(從下往上):1011

注意:只要遇到除以后的結果為0就結束了喧兄。所有的整數(shù)除以2一定能夠最終得到0勋桶,但是小數(shù)就不能,小數(shù)轉變?yōu)槎M制的算法就有可能會無限循環(huán)下去露乏。

2.十進制小數(shù)轉為二進制數(shù)

算法是乘以2知道沒有了小數(shù)為止车海,例子:

0.9表示成二進制數(shù):

0.9*2 = 1.8 取整數(shù)部分:1

0.8*2 = 1.6 取整數(shù)部分:1

0.6*2 = 1.2 取整數(shù):1

0.2*2 = 0.4 取整數(shù):0

0.4*2 = 0.8 取整數(shù):0

0.8*2 = 1.6 取整數(shù):1

笛园。。侍芝。研铆。

0.9二進制表示為(從上往下):1100100100100.......

注意:上面的計算過程循環(huán)了,也就是說乘以2永遠不能消滅小數(shù)部分竭贩,這樣算法將無限下去蚜印。顯然,小數(shù)的二進制表示有時是不能精確的留量。道理很簡單,十進制系統(tǒng)中不能準確的表示出1/3哟冬,同樣二進制也無法準確的表示1/10楼熄。這也是浮點型出現(xiàn)精度丟失問題的主要原因。

解決方案一:

如果不介意記錄十進制的小數(shù)點浩峡,而且數(shù)值不大可岂,那么可以使用long,int等基本類型,

int resultInt = 10 -9;

double result ?= (double)resultInt / 100;//自己控制小數(shù)點

解決方案二:

使用BigDecimal翰灾,而且需要在構造參數(shù)使用String類型.

float和double只能用來做科學計算或者工程計算缕粹,在商業(yè)計算等精確計算中,要用java.math.BigDecimal纸淮。

在《EffectiveJava》這本書中就給出了一個解決方法平斩。該書中也指出,float和double只能用來做科學計算或者是工程計算咽块,在商業(yè)計算等精確計算中绘面,我們要用java.math.BigDecimal。

BigDecimal類一個有4個方法,我們只關心對我們解決浮點型數(shù)據(jù)進行精確計算有用的方法揭璃,即

BigDecimal(double value) // 將double型數(shù)據(jù)轉換成BigDecimal型數(shù)據(jù)

思路很簡單晚凿,我們先通過BigDecimal(double

value)方法,將double型數(shù)據(jù)轉換成BigDecimal數(shù)據(jù)瘦馍,然后就可以正常進行精確計算了歼秽。等計算完畢后,我們可以對結果做一些處理情组,比如

對除不盡的結果可以進行四舍五入燥筷。最后,再把結果由BigDecimal型數(shù)據(jù)轉換回double型數(shù)據(jù)呻惕。

這個思路很正確荆责,但是如果你仔細看看API里關于BigDecimal的詳細說明,你就會知道亚脆,如果需要精確計算做院,我們不能直接用double,而非要用

String來構造BigDecimal不可濒持!所以键耕,我們又開始關心BigDecimal類的另一個方法,即能夠幫助我們正確完成精確計算的

BigDecimal(String value)方法柑营。

// BigDecimal(String value)能夠將String型數(shù)據(jù)轉換成BigDecimal型數(shù)據(jù)

那么問題來了屈雄,想像一下吧,如果我們要做一個浮點型數(shù)據(jù)的加法運算官套,需要先將兩個浮點數(shù)轉為String型數(shù)據(jù)酒奶,然后用

BigDecimal(String

value)構造成BigDecimal,之后要在其中一個上調(diào)用add方法奶赔,傳入另一個作為參數(shù)惋嚎,然后把運算的結果(BigDecimal)再轉換為浮

點數(shù)。如果每次做浮點型數(shù)據(jù)的計算都要如此站刑,你能夠忍受這么煩瑣的過程嗎另伍?至少我不能。所以最好的辦法绞旅,就是寫一個類摆尝,在類中完成這些繁瑣的轉換過程。這

樣因悲,在我們需要進行浮點型數(shù)據(jù)計算的時候堕汞,只要調(diào)用這個類就可以了。網(wǎng)上已經(jīng)有高手為我們提供了一個工具類Arith來完成這些轉換操作囤捻。它提供以下靜態(tài)

方法臼朗,可以完成浮點型數(shù)據(jù)的加減乘除運算和對其結果進行四舍五入的操作:

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的源代碼邻寿,大家只要把它編譯保存好,要進行浮點數(shù)計算的時候视哑,在你的源程序中導入Arith類就可以使用以上靜態(tài)方法來進行浮點數(shù)的精確計算了绣否。

importjava.math.BigDecimal;

/**

*?由于Java的簡單類型不能夠精確的對浮點數(shù)進行運算,這個工具類提供精

*?確的浮點數(shù)運算挡毅,包括加減乘除和四舍五入蒜撮。

*/

publicclassArith{

//默認除法運算精度

privatestaticfinalintDEF_DIV_SCALE?=10;

//這個類不能實例化

privateArith(){

}

/**

*?提供精確的加法運算。

*?@param?v1?被加數(shù)

*?@param?v2?加數(shù)

*?@return?兩個參數(shù)的和

*/

publicstaticdoubleadd(doublev1,doublev2){

BigDecimal?b1?=newBigDecimal(Double.toString(v1));

BigDecimal?b2?=newBigDecimal(Double.toString(v2));

returnb1.add(b2).doubleValue();

}

/**

*?提供精確的減法運算跪呈。

*?@param?v1?被減數(shù)

*?@param?v2?減數(shù)

*?@return?兩個參數(shù)的差

*/

publicstaticdoublesub(doublev1,doublev2){

BigDecimal?b1?=newBigDecimal(Double.toString(v1));

BigDecimal?b2?=newBigDecimal(Double.toString(v2));

returnb1.subtract(b2).doubleValue();

}

/**

*?提供精確的乘法運算段磨。

*?@param?v1?被乘數(shù)

*?@param?v2?乘數(shù)

*?@return?兩個參數(shù)的積

*/

publicstaticdoublemul(doublev1,doublev2){

BigDecimal?b1?=newBigDecimal(Double.toString(v1));

BigDecimal?b2?=newBigDecimal(Double.toString(v2));

returnb1.multiply(b2).doubleValue();

}

/**

*?提供(相對)精確的除法運算,當發(fā)生除不盡的情況時耗绿,精確到

*?小數(shù)點以后10位苹支,以后的數(shù)字四舍五入。

*?@param?v1?被除數(shù)

*?@param?v2?除數(shù)

*?@return?兩個參數(shù)的商

*/

publicstaticdoublediv(doublev1,doublev2){

returndiv(v1,v2,DEF_DIV_SCALE);

}

/**

*?提供(相對)精確的除法運算误阻。當發(fā)生除不盡的情況時债蜜,由scale參數(shù)指

*?定精度,以后的數(shù)字四舍五入究反。

*?@param?v1?被除數(shù)

*?@param?v2?除數(shù)

*?@param?scale?表示表示需要精確到小數(shù)點以后幾位寻定。

*?@return?兩個參數(shù)的商

*/

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();

}

/**

*?提供精確的小數(shù)位四舍五入處理。

*?@param?v?需要四舍五入的數(shù)字

*?@param?scale?小數(shù)點后保留幾位

*?@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

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末精耐,一起剝皮案震驚了整個濱河市狼速,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卦停,老刑警劉巖向胡,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異惊完,居然都是意外死亡捷枯,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門专执,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人郁油,你說我怎么就攤上這事本股。” “怎么了桐腌?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵拄显,是天一觀的道長。 經(jīng)常有香客問我案站,道長躬审,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮承边,結果婚禮上遭殉,老公的妹妹穿的比我還像新娘。我一直安慰自己博助,他們只是感情好险污,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著富岳,像睡著了一般蛔糯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上窖式,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天蚁飒,我揣著相機與錄音,去河邊找鬼萝喘。 笑死淮逻,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的蜒灰。 我是一名探鬼主播弦蹂,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼强窖!你這毒婦竟也來了凸椿?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤翅溺,失蹤者是張志新(化名)和其女友劉穎脑漫,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咙崎,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡优幸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了褪猛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片网杆。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖伊滋,靈堂內(nèi)的尸體忽然破棺而出碳却,到底是詐尸還是另有隱情,我是刑警寧澤笑旺,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布昼浦,位于F島的核電站,受9級特大地震影響筒主,放射性物質發(fā)生泄漏关噪。R本人自食惡果不足惜鸟蟹,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望使兔。 院中可真熱鬧建钥,春花似錦、人聲如沸火诸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽置蜀。三九已至奈搜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盯荤,已是汗流浹背馋吗。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留秋秤,地道東北人宏粤。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像灼卢,于是被迫代替她去往敵國和親绍哎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容