8,拓寬數值類型會造成精度丟失嗎?
Java語言的8種基本數據類型中7種都可以看作是數值類型,我們知道對於數值類型的轉換有一個規律:從窄范圍轉化成寬范圍能夠自動類型轉換,反之則必須強制轉換。請看下圖:P=I85C
byte-->short-->int-->long-->float-->double%<
char-->int"G
我們把順箭頭方向的轉化叫做拓寬類型,逆箭頭方向的轉化叫做窄化類型。一般我們認為因為順箭頭方向的轉化不會有數據和精度的丟失,所以Java語言答應自動轉化,而逆箭頭方向的轉化可能會造成數據和精度的丟失,所以Java語言要求程序員在程序中明確這種轉化,也就是強制轉換。那麼拓寬類型就一定不會造成數據和精度丟失嗎?請看下面代碼:hc/N@
int i=2000000000;n
int num=0;?H=
for(float f=i;f
num++;.}Q}1
}©達內IT技術論壇—中國人學Java、學C++、學C#/.Net、學軟件、學IT的地方 -- 達內科技論壇 ^n{P]W
System.out.println(num);=u
請考察以上代碼輸出多少?C/q+
假如你回答50 ,那麼請運行一下,結果會讓你大吃一驚!沒錯,輸出結果是0,難道這個循環根本就沒有執行哪怕一次?確實如此,假如你還不死心,我帶你你看一個更詫異的現象,運行以下代碼,看輸出什麼?~
int i=2000000000;9
float f1=i;yW
float f2=i+50;g
System.out.println(f1==f2);d
哈哈,你快要不相信你的眼睛了,結果竟然是true;難道f1和f2是相等的嗎?是的,就是這樣,這也就能解釋為什麼上一段代碼輸出的結果是0,而不是50了。那為什麼會這樣呢?要害原因在於你將int值自動提升為float時發生了數據精度的丟失,i的初始值是2000000000,這個值非常接近Integer.MAX_value,因此需要用31位來精確表示,而float只能提供24位數據的精度(另外8位是存儲位權,見IEEE745浮點數存儲規則)。所以在這種自動轉化的過程中,系統會將31位數據的前24位保留下來,而捨棄掉最右邊的7位,所以不管是2000000000還是2000000050,捨棄掉最右邊7位後得到的值是一樣的。這就是為什麼f1==f2的原因了。IwY'q
類似的這種數值拓寬類型的過程中會造成精度丟失的還有兩種情況,那就是long轉化成float和long轉化成double,所以在使用的時候一定要小心。
9,i=i+1和i+=1完全等價嗎?
可能有很多程序員認為i+=1只是i=i+1的簡寫方式,其實不然,它們一個使用簡單賦值運算,一個使用復合賦值運算,而簡單賦值運算和復合賦值運算的最大差別就在於:復合賦值運算符會自動地將運算結果轉型為其左操作數的類型。看看以下的兩種寫法,你就知道它們的差別在哪兒了:1wa
(1) byte i=5;Zvt^o
i+=1;`d.
(2) byte i=5;F1p
i=i+1;J&
第一種寫法編譯沒問題,而第二種寫法卻編譯通不過。原因就在於,當使用復合賦值運算符進行操作時,即使右邊算出的結果是int類型,系統也會將其值轉化為左邊的byte類型,而使用簡單賦值運算時沒有這樣的優待,系統會認為將i+1的值賦給i是將int類型賦給byte,所以要求強制轉換。理解了這一點後,我們再來看一個例子:@4DiYK
byte b=120;e-
b+=20;6(dTP
System.out.println("b="+b);+e6_+
說到這裡你應該明白了,上例中輸出b的值不是140,而是-116。因為120+20的值已經超出了一個byte表示的范圍,而當我們使用復合賦值運算時系統會自動作類型的轉化,將140強轉成byte,所以得到是-116。由此可見,在使用復合賦值運算符時還得小心,因為這種類型轉換是在不知不覺中進行的,所以得到的結果就有可能和你的預想不一樣。