我了解Java中无符号右移运算符“>>>”的作用,但是为什么我们需要它,为什么我们不需要相应的无符号左移运算符?
6 回答
该>>>
运算符允许您将int
和long
视为 32 位和 64 位无符号整数类型,而 Java 语言中缺少这些类型。
当您移动不代表数值的东西时,这很有用。例如,您可以使用 32 位 s 表示黑白位图图像int
,其中每个位图int
在屏幕上编码 32 个像素。如果您需要将图像向右滚动,您希望 an 左侧的位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;
如果整数high
和low
接近于最大的机器整数,上述将是正确的,但
int mid = (low + high) / 2;
由于溢出可能会得到错误的结果。
这是一个使用示例,修复了幼稚二进制搜索中的错误。
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
将产生“负”值,但由于已知算术正确的值是正的,因此使用无符号右移将允许使用正确的正数。
基本上这与符号(数字移位)或无符号移位(通常与像素相关的东西)有关。
由于左移,无论如何都不处理符号位,它是同一件事(<<<和<<)......
不管怎样,我还没有遇到任何需要使用 >>> 的人,但我确信他们在那里做着令人惊奇的事情。
如您所见,>> 运算符在每次发生移位时自动用其先前的内容填充高位。这保留了值的符号。然而,有时这是不可取的。例如,如果您正在移动不代表数值的东西,您可能不希望发生符号扩展。当您使用基于像素的值和图形时,这种情况很常见。在这些情况下,您通常希望将零移到高位,无论其初始值是什么。这称为无符号移位。为此,您将使用 java 的无符号右移运算符 >>>,它总是将零移到高位。
进一步阅读:
http://henkelmann.eu/2011/02/01/java_the_unsigned_right_shift_operator
负数的正常右移>>
将保持负数。即符号位将被保留。
无符号右移>>>
也会移动符号位,用零位替换它。
没有必要进行等效的左移,因为只有一个符号位并且它是最左边的位,所以它只会在右移时干扰。
本质上,区别在于一个保留符号位,另一个移入零以替换符号位。
对于正数,它们的作用相同。
有关使用两者的示例,>>
请>>>
参阅BigInteger shiftRight。
在 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))));