浮點(diǎn)數(shù)

1,浮點(diǎn)數(shù)基本知識(shí)

Java 語言支持兩種基本的浮點(diǎn)類型: float 和 double ,以及與它們對(duì)應(yīng)的包裝類 Float 和 Double 。它們都依據(jù) IEEE 754 標(biāo)準(zhǔn),該標(biāo)準(zhǔn)定義了32 位單精度和 64 位雙精度兩種浮點(diǎn)二進(jìn)制小數(shù)標(biāo)準(zhǔn)。

IEEE 754 用科學(xué)記數(shù)法以底數(shù)為 2 的小數(shù)來表示浮點(diǎn)數(shù)。

  • 32位單精度浮點(diǎn)數(shù)float,用 1 位表示數(shù)字的符號(hào),用 8 位表示指數(shù),用 23 位表示尾數(shù),即小數(shù)部分,如2^23 = 8388608,一共七位,這意味著最多能有7位有效數(shù)字,但絕對(duì)能保證的為6位,也即float的精度為6~7位有效數(shù)字。
  • 64位雙精度浮點(diǎn)數(shù)double,用1 位表示數(shù)字的符號(hào),用 11 位表示指數(shù),用52 位表示尾數(shù),即小數(shù)部分,如2^52 = 4503599627370496,一共16位,也即double的精度為15~16位。
  • 作為有符號(hào)整數(shù)的指數(shù)可以有正負(fù)之分,也即決定了浮點(diǎn)數(shù)的取值范圍。小數(shù)部分用二進(jìn)制(底數(shù) 2)小數(shù)來表示,這意味著最高位對(duì)應(yīng)著值 ?(2 -1),第二位對(duì)應(yīng)著 ?(2 -2),依此類推。

IEEE 浮點(diǎn)值的格式如圖 1 所示:

圖 1. IEEE 754 浮點(diǎn)數(shù)的格式

2,浮點(diǎn)數(shù)比較

在java中浮點(diǎn)型默認(rèn)是double的,浮點(diǎn)數(shù)都要在計(jì)算機(jī)里轉(zhuǎn)換進(jìn)行二進(jìn)制存儲(chǔ),這就涉及到數(shù)據(jù)精度。
例如,十進(jìn)制小數(shù)0.9表示成二進(jìn)制數(shù)為:1100100100100......后面部分將無限循環(huán)下去,很顯然,小數(shù)的二進(jìn)制表示有時(shí)是不可能精確的。比如double類型表示小數(shù)部分只有52位,當(dāng)向后計(jì)算52位后基數(shù)還不為0,那后面的部分只能通過四舍五入得到一個(gè)近似值,此時(shí)就造成精度丟失。
所以,當(dāng)浮點(diǎn)數(shù)進(jìn)行比較時(shí),由于浮點(diǎn)數(shù)的二進(jìn)制表示本身就不準(zhǔn)確,比較大小時(shí)就會(huì)出現(xiàn)錯(cuò)誤。例如float f1 = 20014999f == float f2 = 20015000f。

因此,有個(gè)原則:

  • 程序中應(yīng)盡量避免浮點(diǎn)數(shù)的比較
  • float、double類型的運(yùn)算往往都不準(zhǔn)確

3,BigDecimal

要想獲得理想的結(jié)果,應(yīng)該使用BigDecimal來獲得更精確的計(jì)算。
在《Effective Java》這本書中也提到這個(gè)原則,float和double只能用來做科學(xué)計(jì)算或者是工程計(jì)算,在商業(yè)計(jì)算中我們要用java.math.BigDecimal。

BigDecimal夠造方法的參數(shù)類型有4種,其中的兩個(gè)用BigInteger構(gòu)造,另一個(gè)是用double構(gòu)造,還有一個(gè)使用String構(gòu)造。應(yīng)該避免使用double構(gòu)造BigDecimal,因?yàn)椋河行?shù)字用double根本無法精確表示,傳給BigDecimal構(gòu)造方法時(shí)就已經(jīng)不精確了。
比如,new BigDecimal(0.1)得到的值是0.1000000000000000055511151231257827021181583404541015625。使用new BigDecimal("0.1")得到的值是0.1。因此,如果需要精確計(jì)算,用String構(gòu)造BigDecimal,避免用double構(gòu)造。

BigDecimal都是不可變的(immutable)的,在進(jìn)行每一步運(yùn)算時(shí),都會(huì)產(chǎn)生一個(gè)新的對(duì)象,由于創(chuàng)建對(duì)象會(huì)引起開銷,因此它們不適合于大量的數(shù)學(xué)運(yùn)算,所以a.add(b);雖然做了加法操作,但是a并沒有保存加操作后的值,正確的用法應(yīng)該是a=a.add(b)。

4,小數(shù)點(diǎn)位數(shù)保留

  • String s=String.format("%.2f",d),表示保留小數(shù)點(diǎn)后任意兩位小數(shù),并且符合四舍五入的規(guī)則。
  • DecimalFormat df = new DecimalFormat("0.00"),不管傳入的任何值,均保留兩位小數(shù)。
  • DecimalFormat df = new DecimalFormat("#.##"),則保留小數(shù)點(diǎn)后面不為0的兩位小數(shù),這種寫法不能保證保留2為小數(shù),但能保證最后一位數(shù)不為0。
  • double d = 1.000;
    BigDecimal bd=new BigDecimal(d);
    double d1=bd.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
    System.out.println(d1);
    輸出結(jié)果:1.0
    若double d=0,輸出結(jié)果為0.0;
    若double d=1.999,輸出結(jié)果為2.0;
    若double d=1.89,輸出結(jié)果為1.89;
    這種寫法若小數(shù)點(diǎn)后均為零,則保留一位小數(shù),并且有四舍五入的規(guī)則。

參考:
https://www.ibm.com/developerworks/cn/java/j-jtp0114/index.html
http://blog.csdn.net/ccecwg/article/details/22286873
http://www.cnblogs.com/chenfei0801/p/3672177.html
http://www.ituring.com.cn/article/216160
http://swiftlet.net/archives/798
http://m.itdecent.cn/p/00fff555986b

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容