bit-manipulation java无符号左移 - Java:对负数进行右移





java位运算 右移一位 (4)


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

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

无符号移位>>>不会保持符号位(因此填充0 s)。

(图片来自维基百科)

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

关于负数的右移操作我很困惑,这里是代码。

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

结果是:

11111111111111111111111111110001
11111111111111111111111111111111

为什么要将负数移31而不是1(符号位)?




运算符>>调用右移位 ,将所有位向右移位指定的次数。 重要的是>>将最左边的符号位(最高有效位MSB)填充到移位后最左边的位。 这称为符号扩展 ,用于在向右移动时保留负数的符号

下面是我的图表表示,其中的示例显示了它是如何工作的(对于一个字节):

例:

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

五分之二的补充形式是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

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

另外由于三个右移,最右边的三个比特都输了。

右两个箭头之间的位从-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位,结果是所有位1 ,实际上是-1的大小。

你注意到这样-1 >> n相当于不是一个陈述。
我相信如果一个人做i = -1 >> n它应该被Java编译器优化为i = -1 ,但这是不同的事情

接下来,在Java中知道更多右移位运算符可用>>>称为无符号右移是很有趣的。 并且它在逻辑上有效,并且每次换档操作从左侧填充零。 因此,如果对正数和正数使用无符号右移>>>运算符,则在每个右移位置总是在最左侧位置获得零位。

例:

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

以下是我的图表,演示表达式-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(图形编程)中很有用,虽然我没有使用它但是在过去的某些地方读过它。

我建议你读一下: >>>和>>之间的区别>>是算术右移, >>>是逻辑右移。

编辑

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

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

  • >><< ,operator >>>运算符也从不抛出异常。

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

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

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

  • 如果左操作数的转换类型是int,则只使用右操作数值的五个最低有效位作为移位距离。 ( 即2 5 = 32位= int中的位数
    因此,移位距离在0到31的范围内。

    这里, r >>> s产生的值与:

    s==0 ? r : (r >> s) & ~(-1<<(32-s))
    
  • 如果左操作数的类型很长,则只使用右操作数值的六个最低有效位作为移位距离。( 即2 5 = 64位=长位数

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

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

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




因为>>被定义为算术右移,它保留了符号。 要获得预期效果,请使用逻辑右移>>>运算符。




For performance, you very often have to do some compromsies. Others have expressed various methods, however, you noted Carmack's hack was faster up to certain values of N. Then, you should check the "n" and if it is less than that number N, use Carmack's hack, else use some other method described in the answers here.





java bit-manipulation bitwise-operators bit-shift negative-number