Java:在负数上右移

我对负号右移操作很困惑,这里是代码。

int n = -15; System.out.println(Integer.toBinaryString(n)); int mask = n >> 31; System.out.println(Integer.toBinaryString(mask)); 

结果是:

 11111111111111111111111111110001 11111111111111111111111111111111 

为什么右移一个负数不是1(符号位)?

因为在Java中没有无符号的数据types,所以有两种types的右移: 算术移位 >>和逻辑移位 >>>http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

算术移位>>将保持符号位。
算术转换

无符号移位>>>将不会保留符号位(因此填充0 s)。
逻辑转移

(来自维基百科的图片)


顺便说一下,算术左移和逻辑左移都有相同的结果,所以只有一个左移<<

操作员>>称为右移 ,将所有位右移指定的次数。 重要的是>>填充最左边的符号位(最高有效位MSB)到最左边的位后移。 这就是所谓的符号扩展 ,当你把它们移到正确的位置时,它可以保留负数的符号

下面是我的示意图,用一个例子来说明这是如何工作的(对于一个字节):

例:

 i = -5 >> 3; shift bits right three time 

5的补码forms是1111 1011

内存表示:

  MSB +----+----+----+---+---+---+---+---+ | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | +----+----+----+---+---+---+---+---+ 7 6 5 4 3 2 1 0 ^ This seventh, the left most bit is SIGN bit 

下面是,如何>>工作? 当你做-5 >> 3

  this 3 bits are shifted out and loss MSB (___________) +----+----+----+---+---+---+---+---+ | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | +----+----+----+---+---+---+---+---+ | \ \ | ------------| ----------| | | | ▼ ▼ ▼ +----+----+----+---+---+---+---+---+ | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | +----+----+----+---+---+---+---+---+ (______________) The sign is propagated 

注意:最左边的三位是一个,因为每个移位符号位被保留,每一位都是正确的。 我写了这个符号被传播,因为所有这三个位都是因为符号(而不是数据)。

另外由于三权右移最多的三位都是亏损的。

右两个箭头之间的位从前面的位-5中暴露出来。

我认为如果我也为正数编写一个例子,这将是一件好事。 下一个例子是5 >> 3 ,五个是一个字节是0000 0101

  this 3 bits are shifted out and loss MSB (___________) +----+----+----+---+---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | +----+----+----+---+---+---+---+---+ | \ \ | ------------| ----------| | | | ▼ ▼ ▼ +----+----+----+---+---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +----+----+----+---+---+---+---+---+ (______________) The sign is propagated 

再次看到我写的符号传播 ,所以最左边的三个零是由于签名位。

所以这就是运营商>> 签名右移做,保留左操作数的符号。

[你的答案]
在你的代码中,你使用>>运算符将-15右移31次,所以你的最右边的31位被释放,结果都是实际为-11

你是否注意到这样-1 >> n等于不是一个声明。
我相信如果一个人做i = -1 >> n它应该优化到i = -1的Java编译器,但这是不同的问题

接下来,在Java中知道更多的右移运算符是有用的>>>称为无符号右移 。 它在逻辑上工作并且从每次换class操作的左侧填充零。 因此,在每次右移时,如果对正负数都使用无符号右移>>>运算符,则总是会在最左边的位置得到一个零位。

例:

 i = -5 >>> 3; Unsigned shift bits right three time 

下面是我的图演示如何expression式-5 >>> 3作品?

  this 3 bits are shifted out and loss MSB (___________) +----+----+----+---+---+---+---+---+ | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | +----+----+----+---+---+---+---+---+ | \ \ | ------------| ----------| | | | ▼ ▼ ▼ +----+----+----+---+---+---+---+---+ | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | +----+----+----+---+---+---+---+---+ (______________) These zeros are inserted 

你可以注意到:这一次我不写符号位传播,但实际上>>>运算符插入零。 因此>>>不会保留符号,而是做逻辑右移。

据我所知,无符号右移在VDU(graphics编程)中是有用的,尽pipe我没有使用它,但是在过去的某些地方读过它。

我build议你阅读这个: >>>和>>之间的区别>>是算术右移, >>>是逻辑右移。

编辑

一些有趣的关于无符号右移运算符>>>运算符。

  • 无符号的右移运算符>>>产生一个纯粹的值,它的左操作数右移0 0扩展右操作数指定的位数。

  • >><< ,运算符>>>运算符也不会抛出exception。

  • 无符号右移运算符的每个操作数的types必须是整型数据types,否则会发生编译时错误。

  • >>>操作符可以对其操作数执行types转换; 与算术二元运算符不同,每个操作数都是独立转换的。 如果操作数的types是byte,short或char,则在计算运算符的值之前,将该操作数转换为int。

  • 无符号右移运算符生成的值的types是其左操作数的types。 LEFT_OPERAND >>> RHIGT_OPERAND

  • 如果左操作数的转换types是int,则只有右操作数值的五个最低有效位用作移位距离。 ( 即2 5 = 32位= int中的位数
    所以移动距离在0到31之间。

    这里, r >>> s产生的值与下面的一样:

     s==0 ? r : (r >> s) & ~(-1<<(32-s)) 
  • 如果左操作数的types很长,那么只有右操作数值的6个最低有效位被用作移位距离( 即2 5 = 64位=长位数

    这里, r >>> s产生的值与下面相同:

     s==0 ? r : (r >> s) & ~(-1<<(64-s)) 

一个有趣的参考: [第4章] 4.7移位运算符

因为>>被定义为一个算术右移,保留了符号。 为了达到预期的效果,请使用逻辑右移>>>操作符。