LASSIC Media らしくメディア
ビット演算の基礎|論理演算とシフトの仕組み
ビット演算とは何か
コンピュータの内部では、あらゆる数値が0と1の並び(ビット列)として保持されています。ビット演算とは、この0と1の並びを1桁(1ビット)ずつ取り出し、桁ごとに論理演算や位置の移動を行う計算のことです。普段プログラムで書く「1+1」のような四則演算とは異なり、桁上がりや繰り下がりを気にせず、各ビットを独立に処理する点が特徴です。
四則演算が10進数の感覚に近い「数としての計算」であるのに対し、ビット演算は「並びとしての操作」だと捉えると理解しやすいでしょう。1100という4ビットの並びを12という数値として扱うのが四則演算の世界であり、同じ1100を4つの0/1が並んだものとしてそのまま操作するのがビット演算の世界になります。両者は同じ2進数のビット列を対象にしていても、着目する視点がまったく異なる、別系統の道具だと考えておくと整理しやすいものです。
なお、負の数をどうビットで表すかという符号付き整数の表現方法(2の補数)や、小数と誤差を扱う浮動小数点数、アルゴリズムの効率を測る計算量(Big-O)は、いずれも「数値をどう表すか・どう評価するか」というテーマであり、本記事の対象ではありません。本記事はあくまで、ビット単位の論理演算とシフト演算という「操作そのもの」に焦点を当てて解説します。
それでも、複数の設定値をまとめて扱う設計や、限られたメモリ・通信量の中で状態を表現したい場面では、この「ビット単位で見る」という発想そのものが今なお有効な考え方であり続けています。
アプリケーション開発の現場でビット演算を直接書く機会は限られますが、フラグ管理やパフォーマンスが求められる処理の内部では今も広く使われている仕組みです。発注担当者やプロジェクトマネージャーの立場でも、設計書やコードレビューで「ビットマスク」「フラグ」といった言葉に出会った際に仕組みを理解できていると、実装内容の妥当性を判断しやすくなるでしょう。
論理演算(AND・OR・XOR・NOT)
ビット単位の論理演算には、AND(論理積)・OR(論理和)・XOR(排他的論理和)・NOT(否定)の4種類があります。いずれも2進数1桁(0または1)を入力として、決まった規則で0か1を出力する演算です。まずは1ビットどうしの組み合わせを、1つの対比表で整理します。
| 入力A | 入力B | AND(A&B) | OR(A|B) | XOR(A^B) |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
この表から、それぞれの演算の性質を次のように言い表せます。
- AND(論理積): 両方のビットが1のときだけ1になります。「両方1」を条件とする演算です。
- OR(論理和): どちらか一方でも1であれば1になります。「少なくとも一方が1」であれば成立するものです。
- XOR(排他的論理和): 2つのビットが異なるときだけ1になり、同じ値どうしなら0になります。「異なれば1」という演算だと言えるでしょう。
- NOT(否定): 入力は1つで、0と1を単純に反転させます。0は1に、1は0になります。
実際のプログラムでは1ビットだけでなく、複数ビットの並び全体に対して、同じ位置どうしをまとめて演算します。1100と1010という2つの4ビットの並びを例に、各演算の結果を確認してみましょう。
| 演算 | 計算式 | 結果 |
|---|---|---|
| AND | 1100 & 1010 | 1000 |
| OR | 1100 | 1010 | 1110 |
| XOR | 1100 ^ 1010 | 0110 |
| NOT | NOT 1100(4ビットの場合) | 0011 |
AND(1100 & 1010 = 1000)では、先頭の桁だけ両方が1であるため1となり、残りの3桁はどこかが0なので0になっています。OR(1100 | 1010 = 1110)は、4桁のうち少なくとも一方が1である桁がすべて1になり、両方0の最後の桁のみ0として残るものです。XOR(1100 ^ 1010 = 0110)は、上位2桁が「1と1」「1と0」で異なるかどうかを見ると分かりやすく、値が一致する桁は0、異なる桁は1になっています。このように、同じ2つのビット列を使っても、演算の種類によって結果がまったく異なる点が、論理演算を使いこなすうえでの土台になります。
シフト演算(左シフトと右シフト)
シフト演算とは、ビット列全体を左右にずらす演算です。プログラミング言語では一般に、左シフトを << 、右シフトを >> という記号で表します。
- 左シフト(<<): ビット列を左へずらし、空いた右側の桁には0を詰めます。10進数で見ると、1回シフトするごとに値がおおむね2倍になる操作です。
- 右シフト(>>): ビット列を右へずらし、はみ出した右端のビットは切り捨てられます。10進数で見ると、1回シフトするごとに値がおおむね1/2(小数点以下切り捨て)になる操作です。
4ビットの0011(10進数で3)を例に見てみましょう。左に1回シフトすると0110(10進数で6)になり、値がちょうど2倍になっています。逆に0110(6)を右に1回シフトすると0011(3)に戻り、値が半分になったことが分かるものです。桁を1つずらすだけで乗算・除算に近い結果が得られる点は、シフト演算ならではの性質だと言えるでしょう。
| 操作 | 計算式 | 結果(2進数) | 結果(10進数) |
|---|---|---|---|
| 左シフト | 0011 << 1 | 0110 | 3 → 6 |
| 右シフト | 0110 >> 1 | 0011 | 6 → 3 |
なお、右シフトには大きく分けて「論理シフト」と「算術シフト」の2種類があります。空いた桁に単純に0を詰める論理シフトに対し、符号付きの負の数を扱う算術シフトでは、符号を保つために空いた桁へ元の符号ビットをコピーする、という違いがある点だけ押さえておくとよいでしょう。負の数の内部表現そのものに関わる詳しい仕組みは、符号付き整数の表現方法(2の補数)を扱う別テーマになります。
シフト演算は乗除算の置き換えだけでなく、複数の値を1つの整数にまとめて格納する用途でもよく使われます。たとえば8ビットずつ4つの値を1つの32ビット整数に詰め込みたい場合、それぞれの値を8・16・24ビット分だけ左シフトしてからORで足し合わせる、という組み立て方をします。逆に取り出す際は、目的の位置まで右シフトしたうえでANDによって不要な上位ビットを切り捨てる、という手順を踏むものです。後述する画像処理での色情報の扱いは、この考え方が実際に使われている代表例だと言えるでしょう。
実務での使いどころ
ビット演算は数学的な演習だけの話ではなく、実際のソフトウェアの内部で今も広く活用されています。代表的な用途は次のとおりです。
- ビットフラグ・ビットマスク: 複数のON/OFF(真偽値)を、1つの整数の各ビットに割り当てて管理する手法です。「読み取り可」「書き込み可」「実行可」のような複数の権限や状態を、1つの整数変数だけでまとめて表現できます。
- 権限やオプションの集合表現: OSのファイルパーミッションや、機能フラグ(feature flag)の管理などで、複数のオプションのON/OFFを1つの数値にまとめて持たせる設計に使われるものです。
- 高速な乗除算: 2の累乗倍の乗算・除算は、シフト演算に置き換えることで、通常の掛け算・割り算より軽い処理になる場合があります。
- ハッシュ値・チェックサムの計算: データの改ざん検知や重複チェックに使うハッシュ値、通信エラーを検出するチェックサムの計算過程では、AND・OR・XOR・シフトの組み合わせが多用されています。
- 画像処理: 色情報(RGBなど)を1つの整数にまとめて格納し、シフトやマスクで特定の色成分だけを取り出す・書き換えるといった処理に使われるものです。
この中でも実務で最も出会う頻度が高いのが、ビットフラグ・ビットマスクによる状態管理です。身近な例として、Linux/UnixのファイルパーミッションはREAD(4)・WRITE(2)・EXECUTE(1)という3つのビットの組み合わせで表現されており、次のように整理できます。
| 権限の組み合わせ | ビット(3桁) | 数値 |
|---|---|---|
| 読み取りのみ(R) | 100 | 4 |
| 読み取り+書き込み(R+W) | 110 | 6 |
| 読み取り+実行(R+X) | 101 | 5 |
| すべて(R+W+X) | 111 | 7 |
chmod 755やchmod 644といった数値指定は、まさにこのビットの組み合わせを10進数1桁で表したものです。1つの整数の各ビットに意味を割り当てることで、複数の真偽値を効率よく持ち運べることが、この例からもよく分かります。下図は、READ・WRITE・EXECUTEという3つの権限を1つの整数(3ビット)で表現し、ORで特定のフラグを立て、ANDでフラグが立っているかを調べる流れを示したものです。
利用時の注意点
ビット演算は仕組みを理解すれば強力な道具ですが、実務で扱う際にはいくつか気をつけておきたい点があります。
- 可読性とのバランス: ビット演算は処理としては軽量ですが、コードの読み手にとっては意図が伝わりにくくなりがちです。「なぜこのビットを見ているのか」が分かるよう、定数名やコメントで意味を補っておくことが望ましいでしょう。過度にビット演算を詰め込んだコードは、後任の開発者が保守しづらくなる恐れがあります。
- 符号やオーバーフローとの関係: 符号付き整数に対して右シフトを行う場合、論理シフトか算術シフトかで結果が変わることは前述のとおりです。また、左シフトを繰り返すと桁があふれ(オーバーフロー)、意図しない値になることもあります。負の数の内部表現に関わる詳細は、符号付き整数の表現(2の補数)を扱う別テーマとして整理しておく必要があるでしょう。
- 言語ごとのビット幅の違い: ビット演算が何ビット幅で行われるかは、言語や処理系によって異なります。たとえばJavaScriptのビット演算子は内部的に32ビット整数として扱われ、Javaのintは32ビット・longは64ビットという固定幅を持つ一方、Pythonの整数は桁数に応じて自動的に幅が広がる仕組みです。同じビット演算のコードでも、対象言語のビット幅の扱いを確認しておくことが大切です。
発注担当者やプロジェクトマネージャーの視点では、ビット演算を使った実装そのものを直接レビューする機会は少ないものです。ただ、フラグ管理や権限設計にビットマスクが使われていると聞いた際に、複数の状態を1つの整数にまとめる設計だと理解できていれば、仕様書やテスト観点の妥当性を確認する助けになるでしょう。
特にテスト観点では、ビットフラグを使った設計は「組み合わせのパターン数」が見えにくくなりがちです。3つのフラグを1つの整数で管理している場合、組み合わせは8通り(2の3乗)存在しますが、実装者がその全パターンを意識しないまま一部のケースだけをテストしてしまうと、特定の組み合わせでのみ発生する不具合を見落とす恐れがあります。設計書やテスト計画の段階で、フラグの組み合わせパターンが網羅されているかを確認しておくことも、品質を保つうえで有効な観点です。
まとめ
ビット演算とは、数値を2進数のビット列として捉え、桁ごとに論理演算やシフトを行う計算方法です。AND・OR・XOR・NOTという4種類の論理演算は、それぞれ「両方1」「どちらか1」「異なれば1」「反転」という明確な規則に従っており、1100 AND 1010 = 1000のように、複数ビットの並びにまとめて適用できます。シフト演算は左シフトで値をおおむね2倍、右シフトで値をおおむね1/2にする操作であり、乗除算の高速化にも使われるものです。
実務では、フラグ管理やビットマスクによる権限・オプションの集合表現、ハッシュやチェックサム、画像処理など、パフォーマンスや省メモリ性が求められる場面で今も広く活用されています。一方で可読性や符号・オーバーフロー、言語ごとのビット幅の違いには注意が必要であり、コメントや定数名で意図を補いながら使うことが、保守しやすい実装につながります。
相談するメリット
ビットフラグや権限管理の設計は、いったん動くものができてしまうと、後から仕様変更や機能追加を行う際に「どのビットが何を意味するのか」が分かりにくくなり、改修コストが膨らみやすい領域です。また、シフト演算を使った高速化やチェックサム処理のように、低レイヤの最適化が絡む実装は、コードレビューの中でも見落とされやすいポイントになります。LASSICでは、ニアショア開発体制を生かした受託開発の中で、ビットマスクを用いた権限・フラグ設計のレビューや、パフォーマンスを意識した低レイヤ処理の実装支援に対応しています。設計や既存コードの読み解きでお困りの際は、お気軽にご相談ください。
よくある質問
XORは何に使われますか。
XORは「2つの値が異なるかどうか」を1ビットで表せる性質を持つため、差分検出やチェックサムの計算、簡易的な暗号化・データのマスキング、2つの変数の値を一時変数なしで入れ替える処理などに使われます。同じ値どうしのXORが0になるという性質も、データの一致確認によく利用されるものです。
ビットフラグを使うメリットは何ですか。
複数の真偽値(ON/OFF)を1つの整数にまとめて持てるため、メモリや通信データ量を節約でき、複数の状態を1回の比較・代入でまとめて操作できる点がメリットです。一方で、ビットの意味を定数名やコメントで補っておかないと、コードの可読性が下がりやすい点には注意が必要です。
左シフト・右シフトは常に2倍・1/2になりますか。
左シフトは基本的に値を2倍にしますが、ビット幅を超える桁上がりが起きるとオーバーフローが発生し、想定と異なる値になることがあります。右シフトについても、符号付き整数を扱う場合は論理シフトと算術シフトで結果が異なるため、必ずしも単純な1/2にならないケースがある点に留意しておく必要があります。
ビット演算は今のプログラミングでもまだ使われていますか。
はい、フラグ管理や権限表現、ハッシュ・チェックサムの計算、画像処理や組み込みソフトウェアなど、性能やメモリ効率が重視される領域では現在も広く使われています。高水準なアプリケーション開発では出番が減っているものの、システムの土台となる部分では欠かせない技術です。
著者:テレリモ総研編集部 鈴木 亮佑
ビットマスクを用いたフラグ・権限設計や、パフォーマンスを意識した実装でお困りの際は、ニアショア開発によるコスト最適化と品質確保を両立するLASSICにご相談ください。要件のヒアリングから設計、実装まで伴走いたします。