Java中无符号右移运算符“>>>”的目的是什么?
我理解Java中的无符号右移运算符“>>>”,但为什么我们需要它,为什么我们不需要相应的无符号左移运算符?
>>>
运算符允许您将int
和long
视为32位和64位无符号整数类型,这些类型在Java语言中是缺失的。
当你移动一些不代表数值的东西时,这很有用。 例如,您可以使用32位int
表示黑白位图图像,其中每个int
在屏幕上编码32个像素。 如果你需要向右滚动图像,你会更喜欢int
左边的位变为零,这样你就可以轻松地将相邻int
的位放入:
int shiftBy = 3; int[] imageRow = ... int shiftCarry = 0; // The last shiftBy bits are set to 1, the remaining ones are zero int mask = (1 << shiftBy)-1; for (int i = 0 ; i != imageRow.length ; i++) { // Cut out the shiftBits bits on the right int nextCarry = imageRow & mask; // Do the shift, and move in the carry into the freed upper bits imageRow[i] = (imageRow[i] >>> shiftBy) | (carry << (32-shiftBy)); // Prepare the carry for the next iteration of the loop carry = nextCarry; }
上面的代码没有注意高三位的内容,因为>>>
运算符使它们成为可能
没有相应的<<
运算符,因为有符号和无符号数据类型的左移操作是相同的。
>>>
也是找到两个(大)整数的舍入均值的安全有效的方法:
int mid = (low + high) >>> 1;
如果high
和low
整数接近最大的机器整数,则上述将是正确的
int mid = (low + high) / 2;
由于溢出可能会得到错误的结果。
这是一个使用示例 ,修复了一个天真的二进制搜索中的错误。
基本上这与符号(数字移位)或无符号移位(通常是像素相关的东西)有关。
由于左移,无论如何都不处理符号位,它是一样的(<<<和<<)...
无论哪种方式,我还没有遇到任何需要使用>>>的人,但我确信他们在那里做着惊人的事情。
正如您刚才看到的那样,每次发生移位时,>>运算符会自动用其先前的内容填充高位。 这保留了价值的标志。 但是,有时这是不可取的。 例如,如果要移动不代表数值的内容,则可能不希望进行符号扩展。 当您使用基于像素的值和图形时,这种情况很常见。 在这些情况下,无论初始值是多少,您通常都希望将零移位到高位。 这称为无符号移位。 要实现这一点,您将使用java的无符号,右移运算符>>>,它始终将零移位到高位。
进一步阅读:
http://henkelmann.eu/2011/02/01/java_the_unsigned_right_shift_operator
如果有一个代表一个数字的int
,并且一个希望将它除以2的幂,向负无穷大四舍五入,那么带符号的右移运算符很有用。 在做缩放坐标以进行显示时,这可能很好; 它不仅比分割快,而且在缩放之前因比例因子而不同的坐标之后将相差一个像素。 如果不是使用移位而是使用除法,那将无效。 当按比例缩放2倍时,例如,-1和+1相差2,因此应该在之后相差1,但-1 / 2 = 0且1/2 = 0。 如果相反,使用带符号的右移,事情很好地解决:-1 >> 1 = -1和1 >> 1 = 0,正确地产生相隔一个像素的值。
无符号运算符在输入预期只有一个位设置且一个希望结果也是如此的情况下,或者在一个将使用循环输出一个字中的所有位的情况下是有用的并希望它干净地终止。 例如:
void processBitsLsbFirst(int n, BitProcessor whatever) { while(n != 0) { whatever.processBit(n & 1); n >>>= 1; } }
如果代码使用带符号的右移操作并且传递了负值,则它将无限地输出1。 然而,使用无符号右移运算符,最重要的位最终将被解释为与其他任何位一样。
当计算在算术上产生0到4,294,967,295之间的正数并且希望将该数除以2的幂时,无符号右移算子也可能是有用的。 例如,当计算已知为正的两个int
值的总和时,可以使用(n1+n2)>>>1
而不必将操作数提升为long
。 另外,如果有人希望在不使用浮点数学的情况下将正int
值除以pi之类的值,则可以计算((value*5468522205L) >>> 34)
[(1L << 34)/ pi为5468522204.61,其舍入为圆形收益率5468522205]。 对于超过1686629712的股息, value*5468522205L
的计算将产生“负”值,但由于已知算术上正确的值为正,因此使用无符号右移将允许使用正确的正数。
负数的正常右移>>
将保持负数。 即符号位将被保留。
无符号右移>>>
也会移动符号位,将其替换为零位。
没有必要具有等效的左移,因为只有一个符号位,它是最左边的位,所以它只在右移时干扰。
本质上,不同之处在于,保留符号位,其他以零替换以替换符号位。
对于正数,他们的行为相同。
有关同时使用>>
和>>>
的示例,请参阅BigInteger shiftRight 。
在Java域中,大多数典型应用程序避免溢出的方法是使用cast或Big Integer,例如前面示例中的int to long。
int hiint = 2147483647; System.out.println("mean hiint+hiint/2 = " + ( (((long)hiint+(long)hiint)))/2); System.out.println("mean hiint*2/2 = " + ( (((long)hiint*(long)2)))/2); BigInteger bhiint = BigInteger.valueOf(2147483647); System.out.println("mean bhiint+bhiint/2 = " + (bhiint.add(bhiint).divide(BigInteger.valueOf(2))));