Java中的 ">>>" 表示什么?

69

我在这篇SO帖子中找到了查找重复项的代码。 但是我不理解这一行的含义:int mid = (low + high) >>> 1;

private static int findDuplicate(int[] array) {
        int low = 0;
        int high = array.length - 1;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            System.out.println(mid);
            int midVal = array[mid];

            if (midVal == mid)
                low = mid + 1;
            else
                high = mid - 1;
        }
        return high;
    }

2
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html - Reinstate Monica -- notmaynard
3个回答

141
>>>运算符是Java中的无符号右移位运算符。它有效地将操作数除以右操作数的2次幂,或者这里只是2>>>>>之间的区别仅会在移位负数时显示出来。如果最高位为1,则>>运算符将向最高位移入一个1位,而>>>则移入一个0位。 更新: 让我们求12147483647Integer.MAX_VALUE)的平均值。我们可以轻松地计算:
(1 + 2147483647) / 2 = 2147483648 / 2 = 1073741824

现在,使用代码(low + high) / 2,涉及到以下位数:
          1: 00000000 00000000 00000000 00000001
+2147483647: 01111111 11111111 11111111 11111111
================================================
-2147483648: 10000000 00000000 00000000 00000000  // Overflow
/2
================================================
-1073741824: 11000000 00000000 00000000 00000000  // Signed divide, same as >> 1.

让我们将“shift”更改为 >>>:
          1: 00000000 00000000 00000000 00000001
+2147483647: 01111111 11111111 11111111 11111111
================================================
-2147483648: 10000000 00000000 00000000 00000000  // Overflow
>>> 1
================================================
+1073741824: 01000000 00000000 00000000 00000000  // Unsigned shift right.

2
请问您能否举个例子说明吗?这会非常有帮助。 - eagertoLearn
1
感谢您提供的精彩说明。但在这种情况下,(low+high)/2low + high >>> 1,它们是等价的吗? - eagertoLearn
4
在Java中,如果发生溢出,它们在某些情况下并不等效,正如我所举的例子一样。 - rgettman
1
当发生溢出时,使用无符号右位移运算符,它在技术上相当于除以2并取反符号。 - eagertoLearn
2
是的,>>> 将值视为无符号。如果该值是无符号的,则不会发生溢出。当进行移位操作时,该值不再超过最大的 int 值,因此可以正确地再次解释为有符号值。 - rgettman
其他语言只需要使用>>运算符,因为它们支持无符号整数并推断使用哪种行为,无论是对于无符号的“零填充”行为,还是对于带符号输入时的符号扩展行为。由于Java不支持指定无符号类型,所以在需要访问无符号右移行为时,您需要指定无符号运算符。 - undefined

71

IT技术的意义

int mid = (low + high) >>> 1;

使用无符号移位可以避免溢出导致的负数,这是必要的,因为Java不支持无符号整型值(顺便提一下,char是无符号的)。传统的写法如下:
int mid = (low + high) / 2; // don't do this

然而,对于更大的金额,这可能会溢出,并且您会得到中间数字的负数。

例如:

int high = 2100000000;
int low = 2000000000;
System.out.println("mid using >>> 1 = " + ((low + high) >>> 1));
System.out.println("mid using / 2   = " + ((low + high) / 2));

打印

mid using >>> 1 = 2050000000
mid using / 2   = -97483648

显然,第二个结果是不正确的。

4
+1;非常敏锐(且恰当)地指出字符的unsigned特性。 - Bathsheba
@peter:如果我正在实现二分查找,那么我需要(mid=low+high)/2,有什么方法可以避免这种情况吗? - eagertoLearn
@peter:你能举个例子来展示 >>> 的重要性吗? - eagertoLearn
@user2708477 使用 >>> 1 替代。请参考我的示例。 - Peter Lawrey
使用无符号右移位运算符 >>>,它只是将数字除以2,但当发生溢出时,它会取反符号?感谢提供的示例,我现在明白了。 - eagertoLearn
当发生溢出时,它将符号视为最高位。 - Peter Lawrey

9

这是一个位运算符,作用于二进制数值。 例如,如果A的值为60,则A>>>2将给出15(二进制数值为0000 1111)。

它的实际名称为"零右移位运算符",其中左操作数的值向右移动右操作数指定的位数(在本例中为2),并且移动后的空缺位置填充为零(0000)。


我真的很喜欢你在这里添加的内容。其他答案在解释结果或为什么要进行位移方面做得很好。这对于理解机制非常有帮助! - Brad Ellis
一个更好的解释。 - Shayne3000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接