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 所示:

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