26

負の数での右シフト操作で非常に混乱しています。ここにコードがあります。

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

そして結果は次のとおりです。

11111111111111111111111111110001
11111111111111111111111111111111

なぜ負の数を1(符号ビット)ではなく31だけ右にシフトするのですか?

4

3 に答える 3

40

Java には符号なしデータ型がないため、算術シフト >>論理シフト の 2 種類の右シフトがあり>>>ます。http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

算術シフト>>は符号ビットを保持します。
算術シフト

符号なしシフト>>>は符号ビットを保持しません (したがって、s を埋めます0)。
論理シフト

(ウィキペディアからの画像)


ちなみに、算術左シフトも論理左シフトも結果は同じなので、左シフトは1つだけ<<です。

于 2013-03-17T05:42:59.723 に答える
21

Signed right shift>>と呼ばれる演算子は、指定された回数だけすべてのビットを右にシフトします。重要なのは 、シフト後に左端の符号ビット (Most Significant Bit MSB) を左端のビットに埋めることです。これは符号拡張と呼ばれ、負の数を右にシフトしたときに負の数 の符号を保持するのに役立ちます。>>

以下は、これがどのように機能するかを示す例を図で表したものです (1 バイトの場合):

例:

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

2 の補数形式の 5 は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

注意: 各シフトで符号ビットが保持され、各ビットも右にあるため、左端の 3 ビットは 1 です。この 3 つのビットはすべて符号 (データではない) によるものであるため、 符号が伝搬されます。

また、右に 3 回シフトするため、ほとんどの 3 ビットが失われます。

右の 2 つの矢印の間のビットは、 の前のビットから露出しています-5

正の数の例も書いたらいいと思います。次の例は5 >> 3、5 は 1 バイトです。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

もう一度書きます符号は伝搬されます, したがって、左端の 3 つのゼロは符号ビットによるものです.

したがって、これは演算子>> 符号付き右シフトが行うことであり、左オペランドの符号を保持します。

[あなたの答え]
あなたのコードでは、演算子を使用-15して右にシフトするため、右端のビットが失われ、結果は実際に大きさのすべてのビットになります。31>>311-1

-1 >> nIn this wayは not ステートメントと同等であることに気付き ましたか。
もしそうなら、Javaコンパイラによってi = -1 >> n最適化されるべきだと思いますが、それは別の問題ですi = -1

次に、Java でUnsigned Right Shift>>>と呼ばれる右シフト演算子がもう 1 つ利用可能であることを知っておくと興味深いでしょう。そして、それは論理的に機能し、シフト操作ごとに左からゼロを埋めます。したがって、負数と正数の両方に符号なし右シフト演算子を使用すると、右シフトごとに常に左端の位置にゼロビットが得られます。>>>

例:

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 (グラフィックス プログラミング) で役立ちますが、使用したことはありませんが、過去のどこかで読んだことがあります。

これを読むことをお勧めします: >>> と >> の違い:>>算術右シフト、>>>論理右シフトです。

編集

Unsigned Right Shift Operator >>>operator について興味深いものがあります。

  • 符号なし右シフト演算子は、右オペランドで指定されたビット数だけ拡張>>>ゼロで左オペランドを右シフトした純粋な値を生成します。0

  • and と同様>><<、 operator>>>も operator が例外をスローすることはありません。

  • 符号なし右シフト演算子の各オペランドの型は、整数データ型である必要があります。そうしないと、コンパイル時エラーが発生します。

  • >>>演算子は、そのオペランドに対して型変換を実行できます。算術二項演算子とは異なり、各オペランドは個別に変換されます。オペランドの型が byte、short、または char の場合、そのオペランドは、演算子の値が計算される前に int に変換されます。

  • 符号なし右シフト演算子によって生成される値の型は、その左オペランドの型です。LEFT_OPERAND >>> RHIGT_OPERAND

  • 左オペランドの変換後の型が intの場合、右オペランドの値の最下位 5 ビットのみがシフト距離として使用されます。(つまり、2 5 = 32 ビット = int のビット数) 、シフト距離は 0 から 31 の範囲になります。

    ここで、によって生成される値r >>> sは次と同じです。

    s==0 ? r : (r >> s) & ~(-1<<(32-s))
    
  • 左オペランドの型が long の場合、右オペランドの値の最下位 6 ビットのみがシフト距離として使用されます (つまり、2 5 = 64 ビット = long のビット数) 。

    ここで、によって生成される値r >>> sは次と同じです。

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

興味深いリファレンス: [第 4 章] 4.7 シフト演算子

于 2013-03-17T05:42:01.940 に答える
3

>> は、符号を保持する算術右シフトとして定義されているためです。期待どおりの結果を得るには、論理右シフトである >>> 演算子を使用します。

于 2013-03-17T05:42:35.053 に答える