1. ホーム
  2. language-agnostic

[解決済み】ビットシフト(bit-shift)演算子とは、どのようなもので、どのように機能するのですか?

2022-03-18 08:51:44

質問

私は暇な時にC言語を学ぼうとしているのですが、他の言語(C#、Javaなど)でも同じ概念(同じ演算子であることも多い)があります ...

気になるのは、核心的なレベルでは、ビットシフト( << , >> , >>> は何をするのか、どんな問題を解決できるのか、そしてどんな問題が潜んでいるのか。言い換えれば、ビットシフトの良さをすべて網羅した、まったくの初心者向けのガイドブックです。

どのように解決するのか?

ビットシフト演算子は、その名の通りビットシフトを行う演算子です。 ビットをシフトするのです。 ここでは、さまざまなシフト演算子について簡単に(あるいはあまり簡単に)紹介します。

演算子

  • >> は算術(または符号)右シフト演算子です。
  • >>> は、論理(または符号なし)右シフト演算子です。
  • << は左シフト演算子で、論理シフトと算術シフトの両方のニーズに対応しています。

これらの演算子はすべて、整数値に対して適用することができます ( int , long もしかしたら shortbyte または char ). いくつかの言語では,シフト演算子を int は自動的にオペランドのサイズを変更し int .

なお <<< は冗長になるので演算子ではありません。

また、以下の点にも注意してください。 CとC++は右シフト演算子を区別していない . これらは >> 演算子があり、右シフトの動作は符号付き型に対する実装定義です。 残りの回答は、C# / Javaの演算子を使用しています。

(GCCやClang/LLVMを含むすべての主流のCとC++の実装で。 >> の符号付き型は算術演算です。 これを前提としたコードもありますが、標準が保証しているものではありません。 これは 未定義 しかし、この規格では、実装が何らかの形でそれを定義することを要求しています。 ただし,負の符号付き数値の左シフトは 未定義の動作 (符号付き整数のオーバーフロー) です。 したがって、算術的な右シフトが必要でない限り、通常は符号なし型でビットシフトを行うのがよいでしょう)。


左シフト(<<)

整数は、メモリ上ではビットの連続として保存されます。 例えば、6という数字は32ビットの int となります。

00000000 00000000 00000000 00000110

このビットパターンを1つ左にずらすと( 6 << 1 の場合、12という数字になります。

00000000 00000000 00000000 00001100

見ての通り、桁は1つ左に移動し、右側の最後の桁は0で埋め尽くされています。 また、左シフトは2の累乗と同じであることに注意してください。 6 << 1 は、次のように等価です。 6 * 2 であり、かつ 6 << 3 は、以下のものと同等です。 6 * 8 . 優れた最適化コンパイラは、可能な限り、乗算をシフトに置き換えます。

非円形シフト

なお、これらは ではなく 循環シフト この値を左に1つずらすと( 3,758,096,384 << 1 ):

11100000 00000000 00000000 00000000

の結果は、3,221,225,472 となります。

11000000 00000000 00000000 00000000

端にずれた桁は失われます。 回り込みはしません。


論理右シフト(>>>)

論理的右シフトは、左シフトの逆です。 ビットを左に移動させるのではなく、単純に右に移動させます。 例えば、数字の12をシフトさせる場合です。

00000000 00000000 00000000 00001100

を1つ右に移動させる ( 12 >>> 1 を実行すると、元の6に戻ります。

00000000 00000000 00000000 00000110

つまり、右にシフトすることは2の累乗で割ることと同じであることがわかります。

ロストビットが消える

しかし、シフトは失われたビットを再生することはできません。 例えば、このパターンをシフトさせると

00111000 00000000 00000000 00000110

を左の4つの位置( 939,524,102 << 4 を使用すると、2,147,483,744 になります。

10000000 00000000 00000000 01100000


で、後ろにずれていく( (939,524,102 << 4) >>> 4 となり、134,217,734となります。

00001000 00000000 00000000 00000110

一度失ったビットは、元の値には戻りません。


算術右シフト(>>)

算術右シフトは論理右シフトと全く同じですが、0を詰めるのではなく、最上位ビットを詰めます。 これは、最上位ビットが 符号 ビット、つまり正負の数を区別するためのビットです。 最上位ビットでパディングすることで、算術的右シフトは符号を保持することができます。

例えば、このビットパターンを負の数と解釈した場合。

10000000 00000000 00000000 01100000

という数字は、-214万7,483,552となります。 これを算術シフト(-2,147,483,552 >> 4)で右に4ポジションシフトすると、こうなります。

11111000 00000000 00000000 00000110

または、-134,217,722という数字です。

つまり、論理的な右シフトではなく、算術的な右シフトを使うことで、負の数の符号を維持していることがわかります。 そしてまた、2の累乗による除算を行っていることがわかる。