爲什麼 Java 中的
Integer.MAX_VALUE = 0x7fffffff
,Integer.MIN_VALUE = 0x80000000
? 解決了這個問題,自然也就解決了 爲什麼 Integer.MIN_VALUE 沒有對應的正數。
不難理解上面的最大值(Integer.MAX_VALUE
)以及最小值(Integer.MIN_VALUE
)是 16進制 的表示,轉換成對應的十進制爲:
Integer.MAX_VALUE = 2 ^ 31 - 1
Integer.MIN_VALUE = -2 ^ 31
在 Java 中,short、int、long 在內存中採用了二進制補碼的形式表示,二進制補碼的好處是隻有一個零,沒有 “負零” 的概念,所以可以多存儲一個值。補碼的計算方式如下:
正數的補碼不變,負數的補碼爲符號位不變,其餘取反(反碼),最終加1,0 的補碼仍然爲 0;
迴歸到我們這裏,int 類型佔 32 位,所以轉換成對應的二進制爲(以下均簡寫最大值,最小值):
- max = 0111 1111 1111 1111
- min = 1000 0000 0000 0000
爲什麼 Integer.MAX_VALUE = 2 ^ 31 - 1
這個問題其實是二進制轉換爲十進制的問題,max = 0111 1111 1111 1111 = 2 ^ 0 + 2 ^ 1 + … + 2 ^ 30 (等比數列)= 2 ^ (n + 1) - 1 = 2 ^ 31 -1 。
爲什麼 Integer.MIN_VALUE = -2 ^ 31
這個非常好理解,min = 1000 0000 0000 0000 = - 2 ^ 31 ,那麼爲什麼 min = 1000 0000 0000 0000 呢?我們可以做一個這樣的推算:先看最大的負整數 -1 的二進制補碼形式爲 1000 0001 (中間省略了部分 0),之前說過在內存中採用的是補碼形式,所以我們採用倒推的形式推出原碼:
1000 0001
-0000 0001
=1000 0000,再取反 ,就等於 1111 1111。所以 -1 的二進制形式爲 1111 1111 1111 1111(不省略中間 0 計算的結果)。不難推出 1000 0000 0000 0001 = - ( 2 ^ 31 - 1 ) ,這裏採用了十進制表示。之前有提到過補碼沒有 “負零” 的概念,所以可以多存儲一個值。那麼就是 1000 0000 0000 0000 這個值了,表示爲 十進制則爲
-2 ^ 31。
總結
出現以上問題的根本原因,在於系統中採用的是補碼的方式進行存儲。理解了補碼的特性,也就理解了爲什麼會出現上面的問題了。
這裏還有個有意思的關於 Java 的代碼:
int abs = Math.abs(Integer.MIN_VALUE );
int value = Integer.MIN_VALUE - 1;
上面求出的 abs 仍然爲 Integer.MIN_VALUE
,value 爲 Integer_MAX_VALUE
。
你可以加上這樣的斷言去驗證:
assert Integer.MIN_VALUE == Math.abs(Integer.MIN_VALUE );
assert Integer.MIN_VALUE - 1 == Integer.MAX_VALUE;
至於爲什麼 abs 仍然爲 Integer.MIN_VALUE
,其實絕對值計算可以看做 0 減去這個數,所以上面的計算過程如下(簡化了中間的 0):
0000 0000
-1000 0000
=1000 0000
而 value 會爲什麼會爲 Integer_MAX_VALUE
?這是因爲發生了 溢出 ,底層只會保留 32 位,計算過程如下:
1000 0000
-0000 0001
=0111 1111
現在,終於搞懂爲什麼了!