30

我了解Java中无符号右移运算符“>>>”的作用,但是为什么我们需要它,为什么我们不需要相应的无符号左移运算符?

4

6 回答 6

27

>>>运算符允许您将intlong视为 32 位和 64 位无符号整数类型,而 Java 语言中缺少这些类型。

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

 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;
 }

上面的代码没有注意高三位的内容,因为>>>操作符让他们

没有对应<<的运算符,因为有符号和无符号数据类型的左移操作是相同的。

于 2013-05-26T21:42:32.700 回答
15

>>>也是找到两个(大)整数的舍入平均值的安全有效的方法

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

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

int mid = (low + high) / 2;

由于溢出可能会得到错误的结果。

这是一个使用示例,修复了幼稚二进制搜索中的错误。

于 2014-03-18T01:27:15.877 回答
3

int如果一个人有一个表示数字的 并且希望将其除以 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将产生“负”值,但由于已知算术正确的值是正的,因此使用无符号右移将允许使用正确的正数。

于 2013-09-30T18:03:58.060 回答
3

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

由于左移,无论如何都不处理符号位,它是同一件事(<<<和<<)......

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

如您所见,>> 运算符在每次发生移位时自动用其先前的内容填充高位。这保留了值的符号。然而,有时这是不可取的。例如,如果您正在移动不代表数值的东西,您可能不希望发生符号扩展。当您使用基于像素的值和图形时,这种情况很常见。在这些情况下,您通常希望将零移到高位,无论其初始值是什么。这称为无符号移位。为此,您将使用 java 的无符号右移运算符 >>>,它总是将零移到高位。

进一步阅读:

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

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

于 2013-05-26T21:42:15.017 回答
2

负数的正常右移>>将保持负数。即符号位将被保留。

符号右移>>>也会移动符号位,用零位替换它。

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

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

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

有关使用两者的示例,>>>>>参阅BigInteger shiftRight

于 2013-05-26T22:02:35.983 回答
0

在 Java 领域最典型的应用程序中避免溢出的方法是使用强制转换或 Big Integer,例如前面示例中的 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))));
于 2015-09-21T13:39:29.290 回答