Java 中无符号右移运算符">>>"的目的是什么?



我理解Java中的无符号右移操作符">>>"是什么,但为什么我们需要它,为什么我们不需要相应的无符号左移操作符?

>>>运算符允许您将intlong视为32位和64位的无符号整型,这在Java语言中是缺失的。

当您移动不表示数值的东西时,这很有用。例如,可以使用32位int表示黑白位映射图像,其中每个CC_5对屏幕上的32个像素进行编码。如果您需要向右滚动图像,您希望int左侧的位变为零,以便您可以轻松地将相邻int s中的位:

 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;

如果整数highlow接近最大的机器整数,上述将是正确的,但是

int mid = (low + high) / 2;

会因为溢出而得到错误的结果。

下面是一个使用示例,修复了一个简单二进制搜索中的错误。

对负数进行正常的右移>>将使其保持负值。也就是说,符号位将被保留。

unsigned右移>>>也会移动符号位,将其替换为零位。

不需要进行等效的左移,因为只有一个符号位,而且它是最左边的位,所以它只在右移时干扰。

本质上,区别在于一个保留符号位,另一个移零以替换符号位。

对于正数,它们的作用相同。

同时使用>>>>>的例子见BigInteger shiftRight。

基本上这与符号(数字移位)或无符号移位(通常与像素相关的东西)有关。

由于左移不处理符号位,它是一样的(<<

不管怎样,我还没有遇到任何需要使用>>>的人,但我相信他们正在做着令人惊奇的事情。

正如您刚才看到的,>>操作符自动填充每次发生移位时,具有其先前内容的高阶位。这保留了值的符号。然而,有时这是不受欢迎的。例如,如果你在移动一些不表示一个数值,您可能不希望使用符号扩展的地方。当您使用基于像素的时,这种情况很常见值和图形。在这些情况下,你通常需要移动a不管它的初始值是什么,都把0变成高阶位。这就是所谓的无符号移位。要完成此操作,您将使用Java的无符号右移操作符>>>,它总是移动零到高阶位

进一步阅读:

http://henkelmann.eu/2011/02/01/java_the_unsigned_right_shift_operator

http://www.java-samples.com/showtutorial.php?tutorialid=60

如果有一个int表示一个数字,并且希望将其除以2的幂,四舍五入到负无穷,则有符号右移运算符很有用。这对于缩放显示坐标很有用;它不仅比除法更快,而且在缩放前因比例因子不同的坐标在缩放后将相差一个像素。如果不使用移位,而是使用除法,那就行不通了。例如,当按因子2缩放时,-1和+1相差2,因此之后应该相差1,但是-1/2=0和1/2=0。如果使用带符号的右移,事情会很好地解决:-1>>1=-1和1>>1=0,正确地产生间隔一个像素的值。

unsigned操作符在以下两种情况下都很有用:一是期望输入恰好有一个位,并且希望结果也恰好有一个位;二是使用循环输出单词中的所有位,并希望它干净利落地结束。例如:

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的计算将产生一个"负"值,但由于已知算术正确的值是正数,因此使用无符号右移将允许使用正确的正数。

在Java领域的大多数典型应用程序中,避免溢出的方法是使用类型转换或大整数,例如在前面的示例中将int转换为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))));

最新更新