ビットシフトは、値を数値としてではなく一連のビットとして扱うため、ビット単位の演算と見なされることがあります。 これらの操作では、数字は左または右に移動またはシフトされます。 コンピュータプロセッサのレジスタは固定幅を持っているので、一部のビットは一方の端でレジスタから”シフトアウト”され、同じ数のビットはもう一方の端から”シフトイン”されます。ビットシフト演算子の違いは、シフトインされたビットの値をどのように決定するかにあります。
Bit addressingEdit
レジスタの幅(頻繁には32または64)が、アドレス指定可能な最小単位(原子要素)のビット数(通常は8)よりも大きい場合、頻繁にbyteと呼ばれる、シフト演算はビットにアドレス指定方式を誘導する。レジスタの両端での境界効果を無視すると、算術シフト演算と論理シフト演算は同じように動作し、8ビット位置だけシフトすると、次のようにビッ:
|
8つの位置による左の転位はバイト-アドレスを増加します1 |
|
8つの位置による右の転位はバイト-アドレスを減らします1 |
|
8つの位置による左の転位はバイト-アドレスを減らします1 |
|
8つの位置による右の転位はバイト-アドレスを増加します1 |
算術シフト
算術シフトでは、いずれかの端からシフトされたビットは破棄されます。 左の算術シフトでは、ゼロは右にシフトされます; 右算術シフトでは、符号ビット(2の補数のMSB)が左にシフトされるため、オペランドの符号が保持されます。
この例では、2の補数として解釈される8ビットレジスタを使用します:
00010111 (decimal +23) LEFT-SHIFT= 00101110 (decimal +46)
10010111 (decimal −105) RIGHT-SHIFT= 11001011 (decimal −53)
最初のケースでは、左端の桁がレジスタの最後を過ぎてシフトされ、新しい0が右端の位置にシフトされました。 後者のケースでは、右端の1が(おそらくキャリーフラグに)シフトされ、新しい1が左端の位置にコピーされ、番号の符号が保持されました。 複数のシフトは、いくつかの桁数だけ単一のシフトに短縮されることがあります。 例えば:
00010111 (decimal +23) LEFT-SHIFT-BY-TWO= 01011100 (decimal +92)
nによる左算術シフトは2nを乗算することと同等であり(値がオーバーフローしない場合)、nによる2の補数値の右算術シフトは2nで除算し、負の無限大に向かって四捨五入することと同等である。 二進数が1の補数として扱われる場合、同じ右シフト演算は2nで除算され、ゼロに向かって丸められます。
左論理シフト
|
右論理シフト
|
論理シフトでは、破棄されたビットを置き換えるためにゼロがシフトされます。 したがって、論理的および算術的な左シフトはまったく同じです。
しかし、論理的な右シフトは最上位ビットに値0ビットを挿入するので、符号ビットをコピーする代わりに、符号なし二進数には理想的ですが、算術的な右シフトは符号付き2の補数二進数には理想的です。
Circular shiftEdit
シフトの別の形態は、circular shift、bitwise rotationまたはbit rotationです。
左円形シフトまたは回転
|
右円形シフトまたは回転
|
rotate no carryと呼ばれることもあるこの操作では、レジスタの左端と右端が結合されたかのようにビットが「回転」されます。 左シフト中に右にシフトされる値は、左にシフトされた値であり、右シフト操作の場合はその逆も同様です。 これは、既存のすべてのビットを保持する必要がある場合に便利であり、デジタル暗号化で頻繁に使用されます。
キャリーを介して回転させるedit
左の回転を介して運ぶ
|
右の回転によって運ぶために回して下さい
|
rotate through carryは回転操作の変形であり、シフトされたビット(両端)はcarryフラグの古い値であり、シフトされたビット(もう一方の端)はcarryフラグの新しい値にな
キャリーを介して単一の回転は、事前にキャリーフラグを設定することにより、一つの位置の論理または算術シフトをシミュレートすることができます。 たとえば、carryフラグに0が含まれている場合、x RIGHT-ROTATE-THROUGH-CARRY-BY-ONE
は論理的な右シフトであり、carryフラグに符号ビットのコピーが含まれている場合、x RIGHT-ROTATE-THROUGH-CARRY-BY-ONE
は算術的な右シフトです。 このため、ローエンドのPICsのようないくつかのマイクロコントローラは、単にキャリーを回転させて回転させ、算術命令や論理シフト命令を気にしません。
Rotate through carryは、プロセッサのネイティブ-ワード-サイズよりも大きい数値のシフトを実行するときに特に便利です。 回転によ運ぶことによって、そのビットは余分準備なしで第2転位の間に移ること準備ができた最初の転位の間に運びの旗で”救われる”。
高レベル言語編集
C-family edit
C-family言語では、論理シフト演算子は左シフトの場合は”<<
“、右シフトの場合は”>>
“です。 シフトする場所の数は、演算子の第二引数として与えられます。 例えば,
x = y << 2;
x
y
を2ビット左にシフトした結果を代入します。
シフトは実装定義の動作または未定義の動作になる可能性があるため、それらを使用するときは注意が必要です。 単語のサイズ以上のビット数だけシフトした結果、CおよびC++では未定義の動作になります。 符号付きの値を左シフトした結果が結果の型で表現できない場合、符号付きの値を左シフトした結果は未定義になります。
C#では、最初のオペランドがintまたはlongの場合、右シフトは算術シフトです。 最初のオペランドがuint型またはulong型の場合、右シフトは論理シフトです。
Circular shiftsEdit
言語のCファミリには回転演算子がありませんが、シフト演算子から合成することができます。 セキュリティ要件を持つソフトウェアでの未定義の動作やタイミング攻撃を避けるために、ステートメントが適切に形成されていることを確認す たとえば、32ビットの符号なし値x
をn
位置だけ左回転させる素朴な実装は、単純に次のようになります:
uint32_t x = ..., n = ...;uint32_t y = (x << n) | (x >> (32 - n));
ただし、0
ビットだけシフトすると、32 - 0
は32
であり、32
はの範囲外であるため、右側の式
(x >> (32 - n))
では未定義の動作になります。 第二の試みは、次のようになります:
uint32_t x = ..., n = ...;uint32_t y = n ? (x << n) | (x >> (32 - n)) : x;
シフト量が未定義の動作を導入しないことを確認するためにテストされます。 しかし、ブランチは追加のコードパスを追加し、タイミング解析と攻撃の機会を提供します。 さらに、コードは複数のマシン命令にコンパイルされ、多くの場合、プロセッサのネイティブ命令よりも効率的ではありません。
未定義の動作やGCCとClangの下での分岐を避けるために、以下をお勧めします。 パターンは多くのコンパイラによって認識され、コンパイラは単一のrotate命令を出力します:
uint32_t x = ..., n = ...;uint32_t y = (x << n) | (x >> (-n & 31));
また、microsoft Visual C++の_rotl8、_rotl16、_rotr8、_rotr16などの循環シフトを実装するコンパイラ固有の組み込み関数もあります。 Clangは、上記の問題に苦しんでいるMicrosoft互換性のためのいくつかの回転組込み関数を提供します。 GCCは回転組込み関数を提供していません。 インテルはx86組込み関数も提供しています。Javaでは、すべての整数型が符号付きであるため、”<<
“演算子と”>>
“演算子は算術シフトを実行します。 Javaは論理的な右シフトを実行するために演算子”>>>
“を追加しますが、符号付き整数では論理演算と算術演算の左シフト演算が同じであるため、Javaには”<<<
“演算子はありません。
Javaシフト演算子の詳細:
- 演算子
<<
(左シフト)、>>
(符号付き右シフト)、>>>
(符号なし右シフト)はシフト演算子と呼ばれます。 - シフト式の型は、左側のオペランドの昇格型です。 たとえば、
aByte >>> 2
は((int)aByte)と同等です) >>> 2
. - 左側のオペランドの昇格型がintの場合、右側のオペランドの下位5ビットのみがシフト距離として使用されます。 これは、右側のオペランドがマスク値0x1f(0b11111)を持つビットごとの論理AND演算子&にさらされたかのようです。 したがって、実際に使用されるシフト距離は、常に0から31の範囲に含まれます。
- 左オペランドの昇格型がlongの場合、右オペランドの6つの最下位ビットのみがシフト距離として使用されます。 これは、右側のオペランドがマスク値0x3f(0b111111)を持つビットごとの論理AND演算子&にさらされたかのようです。 したがって、実際に使用されるシフト距離は、常に0から63の範囲に含まれます。
n>>>s
の値は、n個の右シフトされたsビット位置であり、拡張はゼロです。- ビット演算とシフト演算では、型
byte
は暗黙的にint
に変換されます。 Byte値が負の場合、最上位ビットは1であり、intの余分なバイトを埋めるために1が使用されます。 したがって、byte b1=-5;int i=b1|0x0200;
はi==-5
になります。JavaScriptEditJavaScriptは、ビット単位の演算を使用して、2つ以上のユニットのそれぞれを1または0に評価します。Pascalとそのすべての方言(Object PascalやStandard Pascalなど)では、論理的な左シフト演算子と右シフト演算子はそれぞれ”
shl
“と”shr
“です。 符号付き整数の場合でも、shr
は論理シフトのように動作し、符号ビットはコピーされません。 シフトする場所の数は、第二の引数として与えられます。 たとえば、次の例では、yを左に2ビットシフトした結果をxに代入します:x := y shl 2;