JLS 15.19 描述了>>>
运算符的公式。
n>>> s 的值是 n 个右移的 s 位位置,具有 零扩展名,其中:
如果 n 为正,则结果与 n>> s 的结果相同。
如果 n 为负数且左操作数的类型为 int,则 结果等于表达式 (n>> s) + (2 <<~s) 的结果。
如果 n 为负数且左操作数的类型为长整型,则 结果等于表达式 (n>> s) + (2L <<~s) 的结果。
为什么n >>> s = (n >> s) + (2 << ~s)
,哪里~s = 31 - s
int
,~s = 63 - s
长?
如果n
为负数,则表示设置了符号位。
>>> s
意味着将s
位置向右移动,将零引入空出的插槽中。
>> s
意味着将s
位置向右移动,将标志位的副本引入空出的插槽中。
例如
10111110000011111000001111100000 >>> 3 == 00010111110000011111000001111100
10111110000011111000001111100000 >> 3 == 11110111110000011111000001111100
显然,如果n
不是负数,n >> s
和n >>> s
是一样的。如果n
为负数,则差值将由左侧的s
个,后跟所有零组成。
换句话说:
(n >>> s) + X == n >> s (*)
其中X
由 s
个 1 后跟 32 - s
个零组成。
因为X
中有32 - s
个零,所以X
最右边的零出现在1 << (32 - s)
中零的位置,等于2 << (31 - s)
,与2 << ~s
相同(因为~s == -1 - s
和移位量在int
秒内功模32)。
现在,当您将2 << ~s
添加到X
时会发生什么?你得到零!让我们在案例中演示这一点 s == 7
.请注意,进位从左侧消失。
11111110000000000000000000000000
+ 00000010000000000000000000000000
________________________________
00000000000000000000000000000000
因此,-X == 2 << ~s
.因此,在(*)
的两边都增加-X
,我们得到
n >>> s == (n >> s) + (2 << ~s)
对于long
来说,它是完全相同的,除了移位量是模 64,因为long
有 64 位。
这里有一些额外的上下文,如果你还不知道他假设的基础知识,可以帮助你理解pbabcdefp的答案:
要理解按位运算符,您必须将数字视为二进制数字字符串,例如。 20 = 00010100
和-4 = 11111100
(为了清楚起见,不必写那么多数字,我将把所有二进制数写成byte
s; int
s相同,但长度是其四倍)。如果您不熟悉二进制和二进制操作,可以在此处阅读更多内容。注意第一个数字的特殊之处:它使数字为负数,就好像它有一个位值(还记得初等数学,一个/十/几百个位?)最负数,所以Byte.MIN_VALUE = -128 = 1000000
,将任何其他位设置为 1 总是会增加数字。要轻松读取负数,例如 11110011
,知道-1 = 11111111
,然后读取 0,就好像它们是正数中的 1,那么该数字就是您与 -1 的距离。所以11110011 = -1 - 00001100 = -1 - 12 = -13
.
还要了解~s
是按位 NOT 的:它获取所有数字并翻转它们,这实际上相当于 ~s = -1 - s
.例如~5 (00000101)
是-6 (11111010)
。观察我建议的读取负二进制数的方法如何只是一个技巧,能够读取数字的按位 NOT 而不是数字本身,这对于接近零的负数更容易,因为这些数字的 0
秒比 1
秒少。