通過有效地將您可以表示的最大數量有效翻倍來防止溢出。與>>
不同,>>>
不會將符號位視爲特殊符號,所以就此操作而言,您可以使用全部32位來表示數字。
爲了讓示例更容易,讓我們設想一個3字節的帶符號類型,使用2的補碼,就像Java對其整數類型所做的那樣。
0b000 = 0
0b001 = 1
0b010 = 2
0b011 = 3 // MAX
0b100 = -4 // MIN
0b101 = -3
0b110 = -2
0b111 = -1
在二進制補碼中,如果最左邊的位爲零,請將該數字解釋爲正數。如果最左邊的位是一位,那麼這是一個負值;反轉位並添加一個來獲得幅度。
0b101
= -(0b010 + 1)
= -(2 + 1)
= -3
...並且具有作爲明智的方式遞增011
到100
溢出,因爲可以是期望的特性 - MAX
到MIN
,和遞減000
到111
給你-1
。
現在,如果我們在該方案中添加3 + 2,我們溢出到底片:
0b011 + 0b010 = 0b101
這將轉化爲5(正確的),如果我們把它解釋爲一個無符號3位整數。但是由於Java將整數解釋爲二進制補碼,因此它轉換爲-3。
-3/2
給出-1 - 不是我們想要的答案。
-3 >>> 1
給出0b101 >>> 1
給出0b010
,這是2,我們想要的答案。
所以這裏我們使用>>> 1
作爲「除以2,將原始數字視爲無符號整數」。當然,你只能用這個來除以2的冪。
在Java 8中,在java.lang.Integer
和java.lang.Long
中有許多新方法將數字視爲無符號數。一個是divideUnsigned()
其可用於通過任意數量來劃分:
返回其中每個參數和結果被解釋爲一個無符號的值的第二分割的第一個參數的無符號的商。
這可能會有助於您的代碼的未來讀者優先使用此代碼>>>
。
通過使用unsigned-safe方法使用整數,您可以處理大於Integer.MAX_VALUE
- 2^31 -1
的數字。但是如果你超過2^32 -1
,你仍然可以溢出。
最大的Java數組或List
可以是Integer.MAX_VALUE
,所以對於數組或列表索引,(low + high) >>> 1
或Integer.divideUnsigned(low + high, 2)
是安全的。