コンピュータの深層へようこそ!シフト演算子の使い分けをマスターしよう

こんにちは。ゆうせいです。

新人エンジニアのみなさん、Javaの研修は順調ですか?変数や条件分岐に慣れてくると、次に立ちはだかるのがビット演算という壁ですよね。

特に右シフト演算子には >> >>> という、よく似た二つの記号が存在します。

「矢印が二つか三つかだけで、何がそんなに違うの?」

「そもそも、どうして二種類も必要なの?」

そんな疑問を抱いているあなたのために、今日はこの二つの違いを世界一分かりやすく解説します。

ビット演算の基礎知識:符号ビットとは?

右シフトの違いを理解するためには、まずコンピュータが「負の数」をどう表現しているかを知る必要があります。

Javaの整数(int型など)の先頭にある1ビットは、符号ビットと呼ばれています。

この一番左端のビットが 0 ならプラス、 1 ならマイナスを表すというルールです。

例えるなら、マンションの入り口にある「入居中(1)」か「空室(0)」かを示すランプのようなものです。このランプの状態によって、その後に続く数字の意味がガラリと変わってしまいます。

覚えておきたい専門用語

  1. 算術右シフト( >> )符号を維持したまま右にズラす操作です。プラスの数はプラスのまま、マイナスの数はマイナスのまま計算結果を出したいときに使います。
  2. 論理右シフト( >>> )符号なんてお構いなし!とにかく空いた場所に 0 を詰め込む操作です。純粋にビットの並びだけを操作したいときに重宝します。

決定的な違いは「空いたスペースに何をいれるか」

ビットを右にズラすと、左側に「空き地」ができますよね。ここに何を補充するかが運命の分かれ道です。

算術右シフト >> の動き

>> は、一番左にあったビット(符号ビット)と同じ数字を空き地にコピーします。

もし元の数字がマイナス(先頭が 1 )なら、右にズラしても空いた場所には 1 が入ります。これにより、計算後もマイナスの状態がキープされるのです。

数学的に言うと、 2 で割る計算( \div 2 )に近い動きをします。

論理右シフト >>> の動き

一方で >>> は、元の数字がプラスだろうがマイナスだろうが、空いた場所には必ず 0 を叩き込みます!

これを行うと、マイナスの数(先頭が 1 )だったものが、突然プラスの巨大な数に化けてしまいます。なぜなら、符号ビットが強制的に 0 に書き換えられてしまうからです。

メリットとデメリット、そして使い分け

算術右シフト >>

  • メリット負の数の計算を維持できるため、単純な「割り算の代わり」として安心して使えます。
  • デメリットビットのパターンをそのまま操作したい場合には、符号ビットが邪魔になることがあります。

論理右シフト >>>

  • メリット符号に縛られず、純粋なデータとしてビットを扱えます。ハッシュ関数の計算や、色情報の処理などによく使われます。
  • デメリット数値として扱う場合、マイナスの数が予想もしない正の数に変わるため、バグの温床になりやすいです。

実際にコードで比較してみよう

言葉だけではイメージしにくいので、Javaのコードでその差を見てみましょう。

マイナスの数である -1 を使って実験します。

int number = -1; 
// -1はビットで見ると 1111...1111(全て1)です

System.out.println(number >> 1);  
// 結果も -1 (1111...1111 のまま)

System.out.println(number >>> 1); 
// 結果は 2147483647 (0111...1111 になり、正の最大数になる!)

どうですか?たった一本の矢印の差で、結果が天と地ほど変わってしまいましたね!

左シフトの魔術!なぜ << は一つしかないのか?

こんにちは。ゆうせいです。

右シフトの >> >>> の違い、バッチリ理解できましたね!そうなると次に気になるのは、「左シフトには <<< はないの?」という疑問ではないでしょうか。

結論から言うと、Javaに <<< は存在しません。左シフトは << の一種類だけです。

「えっ、片手落ちじゃない?」と思うかもしれませんが、実はこれには非常にシンプルで合理的な理由があるんです。今日はその謎を解き明かしていきましょう!

左シフト << の仕組み

左シフトは、ビットの列を丸ごと左にズラす操作です。

  1. 全てのビットを左に指定した数だけ動かす。
  2. 右側に空いたスペースには、無条件で 0 を詰め込む。
  3. 左端からはみ出したビットは、そのまま捨て去る。

たったこれだけです!右シフトのときのように「符号ビットをコピーするか、 0 を入れるか」という悩みは発生しません。

覚えておきたい専門用語

  1. 算術左シフト数値を 2 のべき乗倍( 2 倍、 4 倍……)にするための操作です。Javaの << はこれに当たります。
  2. オーバーフロービットを左にズラしすぎた結果、大切な符号ビットが書き換わったり、数値が枠からはみ出して消えてしまったりする現象です。「バケツから水が溢れる」様子をイメージしてください。

なぜ左シフトは一種類でいいのか?

右シフトに二種類あったのは、右端に消えていくビットではなく「左側に新しく入ってくるビット」をどう扱うかが問題だったからです。

しかし、左シフトで空き地ができるのは「右端」です。

右端は、数値を表現する上で一番小さな位( 2^{0} の位)ですよね。ここに 1 を入れてしまうと、元の数字が奇数か偶数かという情報がメチャクチャになってしまいます。

一方、どんな数字も 2 倍すれば必ず偶数になります。2進数で「一番下の位が 0 になる」ということは、数学的に正しく 2 倍されたことを意味するのです。

だから、左シフトの空き地には「 0 を入れる」という選択肢以外、あり得ないわけです!

左シフトの威力と注意点

メリット:爆速の掛け算

<< を使うと、コンピュータは一瞬で掛け算を終わらせます。

  • 5 << 1 5 \times 2 = 10
  • 5 << 2 5 \times 4 = 20

という具合です。普通の \times 記号を使うよりも、内部的には非常に軽い処理で済みます。

デメリット:符号が逆転する恐怖

ここが一番の注意点です!

Javaの int 型は32ビットですが、左にズラし続けて 31 回目に到達すると、それまでプラスだった数字が「符号ビット(一番左)」に飛び込んでしまいます。

すると、正の数だったはずが、突然マイナスの数に化けてしまいます。これが「オーバーフロー」の恐ろしさです。

実際にコードで挙動を見てみよう

実際に、プラスの数がマイナスに変わる瞬間をJavaで確認してみましょう。

int target = 1; 
// 2進数で 0000...0001

System.out.println(target << 30); 
// 1073741824(まだ正の数)

System.out.println(target << 31); 
// -2147483648(突然マイナスに!)

一番左の「符号の席」に数値が入り込んでしまったため、コンピュータが「あ、これはマイナスの数だね!」と勘違いしてしまった結果です。

まとめと今後の学習指針

今日の講義のポイントをまとめます。

  • >> は符号を守る(空き地に符号ビットを入れる)。
  • >>> は符号を無視する(空き地に必ず 0 を入れる)。
  • 左シフトは << の一種類だけで、空き地には必ず 0 が入る。
  • 左に 1 つズラすごとに数値は 2 倍になる。
  • ズラしすぎると符号ビットを侵食して、正負が逆転するので要注意!

これからビット演算を使いこなすためには、まず「2の補数」という概念を学んでみてください。なぜマイナスの数が 1 の連続で表現されるのかが分かれば、今回のシフト演算の仕組みがより深く、腑に落ちるはずです。

低レイヤーの知識は、あなたのエンジニアとしての土台を必ず強くしてくれます。一歩ずつ、楽しみながら学んでいきましょう!

セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。

学生時代は趣味と実益を兼ねてリゾートバイトにいそしむ。長野県白馬村に始まり、志賀高原でのスキーインストラクター、沖縄石垣島、北海道トマム。高じてオーストラリアのゴールドコーストでツアーガイドなど。現在は野菜作りにはまっている。