浮點數(shù)

1,浮點數(shù)基本知識

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

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

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

IEEE 浮點值的格式如圖 1 所示:

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

2,浮點數(shù)比較

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

因此,有個原則:

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

3,BigDecimal

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

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

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

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

  • String s=String.format("%.2f",d),表示保留小數(shù)點后任意兩位小數(shù),并且符合四舍五入的規(guī)則。
  • DecimalFormat df = new DecimalFormat("0.00"),不管傳入的任何值,均保留兩位小數(shù)。
  • DecimalFormat df = new DecimalFormat("#.##"),則保留小數(shù)點后面不為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ù)點后均為零,則保留一位小數(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://www.itdecent.cn/p/00fff555986b

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

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

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