web-dev-qa-db-ja.com

Java:負の数で右シフト

私は負の数の右シフト演算で非常に混乱しています、これがコードです。

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

そして結果は:

11111111111111111111111111110001
11111111111111111111111111111111

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

26
Cacheing

Javaには符号なしデータ型がないため、右シフトには2つのタイプがあります: 算術シフト>>および 論理シフト>>>http://docs.Oracle.com/javase/tutorial/Java/nutsandbolts/op3.html

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

符号なしシフト>>>は符号ビットを保持しません(したがって0s)。
Logical shift

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


ちなみに、算術左シフトと論理左シフトはどちらも同じ結果になるため、左シフトは1つだけです<<

36
Alvin Wong

演算子_>>_が呼び出されました符号付き右シフト、指定された回数だけすべてのビットを右にシフトします。重要なのは、シフト後に左端のビットに左端の符号ビット(最上位ビットMSB)を埋める_>>_です。これはsign extensionと呼ばれ、右シフトすると負の数のsignを保持するとして機能します。

以下は、これがどのように機能するかを示すための例を使用した図式表現です(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つのゼロは符号ビットによるものです。

したがって、これは演算子_>>_Signed right shiftが行うことであり、左のオペランドの符号を保持します。

[あなたの答え]
あなたのコードでは、_-15_演算子を使用して_31_演算子を使用して_>>_を_31_だけ右にシフトするので、右端の_1_ビットが緩み、結果はすべて__になります。 (SOMECODE)__これは実際には_-1_の大きさです。

このように_-1 >> n_はステートメントではないことに注意してください。
_i = -1 >> n_を実行する場合、Javaコンパイラーによって_i = -1_に最適化する必要があると思いますが、それは別の問題です

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

>>>と>> の違い:_>>_は算術右シフト、_>>>_は論理右シフトです。

編集

符号なし右シフト演算子_>>>_演算子について興味深い点があります。

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

  • _>>_および_<<_と同様に、演算子_>>>_演算子も例外をスローしません。

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

  • _>>>_演算子は、そのオペランドに対して型変換を実行できます。算術二項演算子とは異なり、各オペランドは個別に変換されます。オペランドのタイプがバイト、ショート、または文字の場合、そのオペランドは、演算子の値が計算される前にintに変換されます。

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

  • 左のオペランドの変換されたタイプがintの場合、右のオペランドの値の5つの最下位ビットのみがシフト距離として使用されます。 (つまり25 = 32ビット= intのビット数
    したがって、シフト距離は0〜31の範囲です。

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

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

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

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

興味深い参照: [第4章] 4.7シフト演算子

14
Grijesh Chauhan

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

3
user207421