Java:负数右移

我对负数的右移操作非常困惑,这是代码。

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

结果是:

11111111111111111111111111110001
11111111111111111111111111111111

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


答案 1

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

算术平移将保留符号位。>>
Arithmetic shift

无符号移位不会保留符号位(从而填充 s)。>>>0
Logical shift

(图片来自维基百科)


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


答案 2

名为“签名右移位”的运算符,将所有位向右移动指定的次数。重要的是在移位后将最左边的符号位(最高有效位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

注意:左边最多三位是一,因为每个移位符号位都保留下来,每个位也是正确的。我已经写了符号传播,因为这三个位都是因为符号(但不是数据)。

此外,由于三个右移右移,大多数三位是损失。

右两个箭头之间的位从 中的上一位公开。-5

我认为如果我也为一个正数写一个例子会很好。下一个示例是,五是一个字节是5 >> 30000 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

再看一遍我写的符号是传播的,所以最左边的三个零是由于符号位。

所以这就是运算符 Signed right shift 所做的,保留了左操作数的符号。>>

[你的答案]
在你的代码中,你使用运算符向右移动,所以你的权利大多数位都是松散的,结果是所有实际上在量级上的位。-1531>>311-1

你是否注意到,以这种方式等同于不是语句。
我相信如果一个人做i = -1>>n,它应该被Java编译器优化为i = -1,但这是另一回事-1 >> n

接下来,有趣的是,在Java中还有一个右移运算符可用,称为Unsign right Shift。它合乎逻辑地工作,并为每个班次操作从左填充零。因此,在每次右移时,如果您对负数和正数都使用无符号的右移位运算符,则大多数位置的左移位始终为零位。>>>>>>

例:

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

  • like and ,运算符也运算符从不引发异常。>><<>>>

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

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

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

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

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

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

    此处,生成的值与以下内容相同:r >>> s

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

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


推荐