关于不能够精确的对浮点数进行运算的问题
http://edu.eoe.cn/?? 在線課堂
昨天看到一篇帖子說了幾個(gè)很明顯的簡(jiǎn)單的浮點(diǎn)的運(yùn)算,計(jì)算機(jī)都會(huì)算錯(cuò)。
我引過來給大家看看:‘
運(yùn)行代碼:
大家可以自己新建一個(gè)工程來試試,結(jié)果如下:
0.060000000000000005 0.5800000000000001 401.49999999999994 1.2329999999999999這就很神奇了,0.05+0.01很明顯是0.06嘛,但是為什么會(huì)變成0.060000000000000005?
是不是計(jì)算器太弱智?真是計(jì)算機(jī)就不靠譜了?
當(dāng)然不是的。主要是因?yàn)閖ava中的簡(jiǎn)單類型并不適用于對(duì)浮點(diǎn)的精確計(jì)算。不光是JAVA ,其它語(yǔ)言也存在同樣的問題。
雖然現(xiàn)在CPU都支持浮點(diǎn)的運(yùn)算了,但是CPU在處理的時(shí)候,也是先把浮點(diǎn)數(shù)(float , double)轉(zhuǎn)成整數(shù)再轉(zhuǎn)成二進(jìn)制,然后進(jìn)行操作,如果有取余,會(huì)有不同的取余方式。
再加上運(yùn)算完成后,再多二進(jìn)制轉(zhuǎn)成上層的浮點(diǎn),又會(huì)有一些取舍。就造成了呈現(xiàn)出來時(shí)的簡(jiǎn)單明顯的錯(cuò)誤 。
所以說,一般float和double用來做科學(xué)計(jì)算或者是工程計(jì)算,在一般對(duì)精度要求較高的地方(如商業(yè)),我們會(huì)用到BCD碼或者是java.math.BigDecimal。
BSD碼(Binary-Coded Decimal),稱BCD碼或二-十進(jìn)制代碼,亦稱二進(jìn)碼十進(jìn)數(shù)。是一種二進(jìn)制的數(shù)字編碼形式,用二進(jìn)制編碼的十進(jìn)制代碼。
大家對(duì)這個(gè)有興趣的可以深入研究一下,今天我們主要講的是BigDecimal類。因?yàn)槲覀冊(cè)谧鲰?xiàng)目的時(shí)候,尤其是對(duì)商業(yè)項(xiàng)目時(shí),我們不可能還去搞個(gè)什么BSD碼,可以直接利用BigDecimal類來完成我們的需求。
BigDecimal 是Java提供的不可變的、任意精度的有符號(hào)十進(jìn)制數(shù)。如果想看更多關(guān)于BigDecimal的介紹,大家可以自行去查看JDK的文檔。
BigDecimal提供了一系列的構(gòu)造函數(shù),主用于將double , string等轉(zhuǎn)化成BigDecimal對(duì)象。
習(xí)慣上我們?cè)谑褂酶↑c(diǎn)數(shù)的時(shí)候都是直接定義的double , float數(shù)據(jù)類型,在定義上是沒有問題,但是如果我們直接調(diào)用BigDecimal(double val) 方法來轉(zhuǎn)化,那我們可得先注意一下JDK文檔中關(guān)于這個(gè)構(gòu)造的詳細(xì)說明了:
直接上中文了,英文好的,可以自己去看原版:
注:
此構(gòu)造方法的結(jié)果有一定的不可預(yù)知性。有人可能認(rèn)為在 Java 中寫入 new BigDecimal(0.1) 所創(chuàng)建的 BigDecimal 正好等于 0.1(非標(biāo)度值 1,其標(biāo)度為 1),但是它實(shí)際上等于 0.1000000000000000055511151231257827021181583404541015625。這是因?yàn)?0.1 無法準(zhǔn)確地表示為 double(或者說對(duì)于該情況,不能表示為任何有限長(zhǎng)度的二進(jìn)制小數(shù))。這樣,傳入 到構(gòu)造方法的值不會(huì)正好等于 0.1(雖然表面上等于該值)。
BigDecimal(double val) 這個(gè)方法是不可預(yù)知的,所以我們推薦使用BigDecimal(String val) 。
String的構(gòu)造函數(shù)就是可預(yù)知的,new BigDecimal(“.1”)如同期望的那樣精確的等于.1。
接下來我們對(duì) 0.05+0.01重新修改一下:
BigDecimal bd1 = new BigDecimal(Double.toString(0.05));BigDecimal bd2 = new BigDecimal(Double.toString(0.01));System.out.println(bd1.add( bd2));我們?cè)賮砜纯催\(yùn)行的結(jié)果:
0.06這下就是我們想要的結(jié)果了,完成了高精度的一個(gè)運(yùn)算了。
注意:
?現(xiàn)在我們已經(jīng)知道怎么樣來解決這個(gè)問題了,原則上是推薦使用BigDecimal(String val) 構(gòu)造方法。
我建議,在商業(yè)的應(yīng)用中,涉及到money的浮點(diǎn)運(yùn)算全都定義成String ,在數(shù)據(jù)庫(kù)中保存也是String ,在需要使用到這個(gè)money來作運(yùn)算的時(shí)候,我們?cè)侔裇tring轉(zhuǎn)化成BigDecimal來完成高精度的運(yùn)算。
????????
試想一下,如果我們要做一個(gè)加法運(yùn)算,需要先將兩個(gè)浮點(diǎn)數(shù)轉(zhuǎn)為String,然后夠造成BigDecimal,在其中一個(gè)上調(diào)用add方法,傳入另一個(gè)作為參數(shù),然后把運(yùn)算的結(jié)果(BigDecimal)再轉(zhuǎn)換為浮點(diǎn)數(shù)。你能夠忍受這么煩瑣的過程嗎?
SO, 網(wǎng)上找了一個(gè)比較好的一個(gè)工具類,封閉了簡(jiǎn)單的操作??梢詤⒖家幌?#xff1a;
?首發(fā)地址:http://www.eoeandroid.com/thread-230579-1-1.html
另外一個(gè)地址:http://krislq.com/150?? 嘎嘎!
轉(zhuǎn)載于:https://www.cnblogs.com/nuliniaoboke/archive/2012/11/21/2780557.html
總結(jié)
以上是生活随笔為你收集整理的关于不能够精确的对浮点数进行运算的问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [ -~] 所有的可打印字符
- 下一篇: 计算机科学主要领域