ビットを左右にずらす操作。左シフトは×2、右シフトは÷2と同じ効果があり、高速な掛け算・割り算に使われます。
シフト演算は、ビット列全体を左または右にスライドさせる操作です。たとえば8ビットの「00001010」(10進で10)を左に1ビットシフトすると「00010100」(10進で20)になります。値がちょうど2倍になっていますね。
左に1ビットシフトすると値が2倍に、右に1ビットシフトすると値が半分になるという、乗除算と密接な関係があります。これはシフト演算の最も重要な性質で、基本情報技術者試験でも頻出します。
シフト演算には論理シフト、算術シフト、循環シフトの3種類があります。それぞれ「空いた桁に何を埋めるか」が異なります。上のツールでスライダーを動かすと、ビットがどう移動し値がどう変わるかリアルタイムで確認できます。
論理シフトは最もシンプルなシフト演算です。ルールはたった1つ:
・ビットを指定方向にスライドし、空いた場所は常に0で埋める
具体例を見てみましょう。8ビットの「11001010」(10進で202)を論理右シフト1ビットすると:
・ビット全体が右に1つずれる
・右端の「0」がはみ出して消える
・左端に0が埋められる
・結果:01100101(10進で101)
202 → 101 で、ちょうど半分になっています。
論理シフトは符号なし整数(unsigned)に使います。符号ビット(先頭ビット)を特別扱いしないため、負の数(先頭が1)に論理右シフトを適用すると、先頭が0に変わって正の数になってしまいます。負の数を正しくシフトしたい場合は、次のカードで説明する「算術シフト」を使います。
上のツールで「論理」を選んで試してみてください。
論理シフトには1つ問題があります。負の数を右シフトすると、符号が変わってしまうのです。例えば -64(11000000)を論理右シフトすると、左側が0で埋められて 00110000(+48)になり、マイナスがプラスに化けます。
算術シフトはこの問題を解決します。右シフト時に空いた左側を0ではなく、元の符号ビット(MSB)と同じ値で埋めるのがポイントです。
・負の数(MSB = 1)→ 空いた場所を1で埋める
・正の数(MSB = 0)→ 空いた場所を0で埋める(論理シフトと同じ結果)
具体例を見てみましょう。8ビットの -64 を算術右シフト2ビットすると:
・シフト前:11000000(-64、MSB = 1)
・右に2ビットずらす:ビット全体が右に移動、右端の2ビットははみ出して消える
・空いた左側2ビットをMSBの1で埋める:11110000(-16)
なぜ2ビットずらすと÷4になるのか?右シフト1ビットは÷2です。2ビットは÷2を2回繰り返すので÷4になります。
・右シフト1ビット = ÷2
・右シフト2ビット = ÷2 × ÷2 = ÷4
・右シフト3ビット = ÷8
・右シフト4ビット = ÷16 ...
一般に、右シフトnビット = ÷2n です。
検算:-64 ÷ 4 = -16 ✓ 負の数のまま正しく割り算できています。論理シフトだと -64 が正の数に化けてしまいますが、算術シフトなら符号が保たれたまま正確に除算できます。
なお、算術左シフトは論理シフトと同じ動作(右側を0で埋める)です。
特に重要なのは「算術右シフトで符号が保存される」という点です。上のツールで「算術」を選び、負の値を右シフトして確認してみてください。
| 操作 | 例 (10) | 結果 | 等価な計算 |
|---|---|---|---|
| 左シフト1 | 10≡00001010 | 20≡00010100 | ×2¹ = ×2 |
| 左シフト2 | 10≡00001010 | 40≡00101000 | ×2² = ×4 |
| 左シフト3 | 10≡00001010 | 80≡01010000 | ×2³ = ×8 |
| 右シフト1 | 10≡00001010 | 5≡00000101 | ÷2¹ = ÷2 |
| 右シフト2 | 10≡00001010 | 2≡00000010 | ÷2² = ÷4 |
左シフトnビットは×2ⁿ、右シフトnビットは÷2ⁿに相当します。なぜでしょうか?10進数で考えてみてください。「15」の各桁を左に1つずらすと「150」になりますが、これは×10です。同様に2進数では各桁を左に1つずらすと×2になるのです。
この性質は乗除算よりも高速に実行できるため、プログラミングの最適化にも使われます。たとえば「×10」は「×8 + ×2」つまり「左3シフト + 左1シフト」で実現できます。上のツールでシフト量を変えて、値がどう変化するか試してみてください。
循環シフト(ローテート)は、論理シフトや算術シフトとは根本的に違います。はみ出したビットを捨てるのではなく、反対側に回り込ませます。ベルトコンベアのように、端まで行ったビットがぐるっと戻ってくるイメージです。
具体例を見てみましょう。8ビットの「10101011」を左循環シフト1ビットすると:
・ビット全体が左に1つずれる
・左端の「1」がはみ出す
・はみ出した「1」が右端に回り込む
・結果:01010111
ビットが1つも失われていないのがポイントです。8ビットを8回循環シフトすると、元の値に完全に戻ります。
上のツールで「循環」を選ぶと、埋めビット(黄色)がはみ出したビットと同じ値になることを確認できます。
「ビットをずらすだけで何の役に立つの?」と思うかもしれませんが、シフト演算はプログラミングの様々な場面で使われています。
CPUにとって、シフト命令は掛け算命令より何倍も速いです。例えば x = 3 を10倍したいとき:
・左シフト3ビット = ×8 なので、3 << 3 = 3 × 8 = 24
・左シフト1ビット = ×2 なので、3 << 1 = 3 × 2 = 6
・24 + 6 = 30(= 3 × 10 ✓)
つまり「×10」という1回の掛け算を、「×8 と ×2 のシフト2回 + 足し算1回」に分解できます。コンパイラがこの最適化を自動で行うこともあります。
1 << 3 で 00001000 が作れます。AND/ORと組み合わせて、特定ビットの検査・セット・クリアを行います。ハードウェア制御やフラグ管理に必須です。
画像の1ピクセルは 0xRRGGBB の24ビット。緑(G)を取り出すには (pixel >> 8) & 0xFF でシフト+マスクします。
| シフト量 | 左シフトの効果 | 右シフトの効果 | 例 (value=3) |
|---|---|---|---|
| 1 | ×2 | ÷2 | 3→6, 3→1 |
| 2 | ×4 | ÷4 | 3→12, 3→0 |
| 3 | ×8 | ÷8 | 3→24, 3→0 |
| 4 | ×16 | ÷16 | 3→48, 3→0 |
nビットのシフトは、1ビットシフトをn回繰り返すのと同じです。左シフトnビットは×2ⁿ、右シフトnビットは÷2ⁿです。たとえば値3を左シフト4ビットすると 3×2⁴ = 3×16 = 48 になります。
右シフトでは小数点以下が切り捨てられる点に注意してください。値3を右シフト2ビットすると 3÷4 = 0.75 ですが、整数のシフトでは結果は0になります。上のツールのシフト量スライダーを動かして、値がどう変化するか試してみてください。
コンピュータが整数を表すとき、ビット列の一番左のビット(=MSB、最上位ビット)が「正か負か」を表す符号ビットになります。
・MSB が 0:正の数
・MSB が 1:負の数
なぜ算術右シフトで符号が保たれるのか。右シフトすると左端に空きが生まれます。論理シフトはその空きを必ず 0 で埋めるため、負の数(MSB=1)を右シフトすると MSB が 0 に変わり、マイナスがプラスに化けてしまいます。算術シフトは空きを元の MSB(符号ビット)と同じ値で埋めるので、正なら 0 埋め・負なら 1 埋めとなり、符号が変わりません。
身近な例で考えると、「マイナスのラベルが貼ってある箱」を右に1段ずらしたとき、左端にできた空き棚にも同じ「マイナスのラベル」を引き継いで貼る——それが算術シフトです。引き継がずに「プラス(0)のラベル」を貼るのが論理シフトです。
| 種類 | 空き埋め | 符号保存 | 主な用途 |
|---|---|---|---|
| 論理シフト | 0 固定 | 保証なし | 符号なし整数の ×÷2 |
| 算術シフト | 符号ビットと同じ | 保たれる | 符号付き整数の ×÷2 |
| 循環シフト | はみ出たビット | 変わりうる | 暗号・ハッシュ計算 |
3つのシフトの違いは「右シフトのときに左端の空きを何で埋めるか」だけです。左シフトはどの種類でも右端を 0 で埋めます(循環シフトを除く)。
・論理シフト:符号に関係なく 0 を詰める。符号なし整数(0以上の整数のみ)に向く
・算術シフト:元の符号ビットを引き継ぐ。負の数を右シフトしても符号が保たれる。符号付き整数に向く
・循環シフト:はみ出したビットが反対側から戻ってくる。情報が一切失われない