1,浮點(diǎn)數(shù)基本知識(shí)
Java 語(yǔ)言支持兩種基本的浮點(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 所示:
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://www.lxweimin.com/p/00fff555986b