|
|
最近無意中看到一本有趣的Java書,《Java解惑》。書中用很多小例子展示了Java編程中的一些少見的陷阱和誤區(qū)。建議學(xué)習(xí)Java基礎(chǔ)的同學(xué)可以好好看看這本書。
舉一個書中最簡單的例子,其實以前很多同學(xué)就問過這個問題,當(dāng)時已經(jīng)逐個解釋。又碰到這個問題的同學(xué)可以看看。
以下程序運行結(jié)果是多少。
class Testdouble{
public static void main(String[] args) {
System.out.println(0.3 - 0.1);
}
}
大家一看可能覺得是0.2,這太簡單了!
實際上答案是
0.19999999999999998
這是為什么呢,取決于java中小數(shù)的存儲方式,其實除了java語言,其他語言也有類似問題。
大部分小數(shù)在計算機中是不精確的。下面引用一片文章:
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
java中的浮點數(shù)采用的事IEEE Standard 754 Floating Point Numbers標(biāo)準(zhǔn),該標(biāo)準(zhǔn)的規(guī)范可以參考http://blog.csdn.net/treeroot/articles/94752.aspx.
float占用4個字節(jié),和int是一樣,也就是32bit.
第1個bit表示符號,0表示正數(shù),1表示負數(shù),這個很好理解,不用多管.
第2-9個bit表示指數(shù),一共8為(可以表示0-255),這里的底數(shù)是2,為了同時表示正數(shù)和負數(shù),這里要減去127的偏移量.這樣的話范圍就是(-127到128),另外全0和全1作為特殊處理,所以直接表示-126到127.
剩下的23位表示小數(shù)部分,這里23位表示了24位的數(shù)字,因為有一個默認的前導(dǎo)1(只有二進制才有這個特性).
最后結(jié)果是 -1)^(sign) * 1.f * 2^(exponent)
這里:sign是符號位,f是23bit的小數(shù)部分,exponent是指數(shù)部分,最后表示范圍是(因為正負數(shù)是對稱的,這里只關(guān)心正數(shù))
2^(-126) ~~ 2(1-2^(-24)) * 2^127
這個還不是float的取值范圍,因為標(biāo)準(zhǔn)中還規(guī)定了非規(guī)格化表示法,另外還有一些特殊規(guī)定.
非規(guī)格化表示:
當(dāng)指數(shù)部分全0而且小數(shù)部分不全0時表示的是非規(guī)格化的浮點數(shù),因為這里默認沒有前導(dǎo)1,而是0.
取值位0.f * 2^(-126),表示范圍位 2^(-149)~~ (1-2^(-23)) * 2^(-126) 這里沒有考慮符號.這里為什么是-126而不是-127 如果是-127的話,那么最大表示為 2^(-127)-2^(-149),很顯然2^(-127) ~~2^(-126) 就沒法表示了.
其他特殊表示
1.當(dāng)指數(shù)部分和小數(shù)部分全為0時,表示0值,有+0和-0之分(符號位決定),0x00000000表示正0,0x80000000表示負0.
2.指數(shù)部分全1,小數(shù)部分全0時,表示無窮大,有正無窮和負無窮,0x7f800000表示正無窮,0xff800000表示負無窮.
3.指數(shù)部分全1,小數(shù)部分不全0時,表示NaN,分為QNaN和SNaN,Java中都是NaN.
結(jié)論:
可以看出浮點數(shù)的取值范圍是:2^(-149)~~(2-2^(-23))*2^127,也就是Float.MIN_VALUE和Float.MAX_VALUE.
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
上面文章說的float型,對于double型是類似的情況。
換句話說,
0.5 在計算機中可以用2進制精確表示,因為它等于1/2
但是0.1 就不能精確表示了,只能用1/16+1/32+....來逼近,
所以大多數(shù)小數(shù)是不精確的。
某些需要高精度運算的場合可以用BigDecimal。 |
|