0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AND・OR・XORは何に使う?権限管理から理解するビット演算とビットマスク

0
Posted at

プログラミングを勉強していると、

AND
OR
XOR
NOT

というビット演算が出てきます。

計算方法だけ見ると、

1 AND 1 = 1
1 OR 0 = 1

のような話で終わってしまい、

「これを実際に何に使うの?」

と感じることがあります。

ビット演算が分かりやすくなる例の一つが、

複数のON/OFF状態を1つの整数で管理する

方法です。

この記事では「ユーザー権限」を例に、AND・OR・XOR・NOTが実際のコードでどう使えるのかを整理します。

4つの権限を考える

あるシステムに、次の4つの権限があるとします。

READ    読み取り
WRITE   書き込み
DELETE  削除
ADMIN   管理者

普通に管理するなら、

const permissions = {
  read: true,
  write: true,
  delete: false,
  admin: false
};

のようにできます。

もちろんこれでも問題ありません。

ただし、それぞれの状態を1bitずつ割り当てると、1つの整数として管理することもできます。

1bitずつ権限を割り当てる

たとえば次のようにします。

READ    = 0001
WRITE   = 0010
DELETE  = 0100
ADMIN   = 1000

10進数では、

READ    = 1
WRITE   = 2
DELETE  = 4
ADMIN   = 8

です。

JavaScriptなら、

const READ   = 1 << 0; // 0001
const WRITE  = 1 << 1; // 0010
const DELETE = 1 << 2; // 0100
const ADMIN  = 1 << 3; // 1000

と書けます。

1 << nは、1を左へnビットシフトするという意味です。

ORで権限を追加する

READとWRITEの両方を持つユーザーを作りたいとします。

READ   0001
WRITE  0010

ORを取ると、

0001
0010
----
0011

になります。

JavaScriptでは、

let permission = READ | WRITE;

console.log(permission);
// 3

です。

2進数では、

0011

なので、

READ  = ON
WRITE = ON

という状態を1つの整数3で表せます。

ORは「フラグを立てる」

既存のユーザーにDELETE権限を追加したい場合もORを使えます。

現在、

0011

だったとします。

DELETEは、

0100

です。

permission = permission | DELETE;

結果は、

0011
0100
----
0111

になります。

つまり、

READ
WRITE
DELETE

の3つがONになりました。

省略して、

permission |= DELETE;

とも書けます。

ANDで権限を確認する

次は、

「このユーザーにWRITE権限があるか?」

を確認します。

現在の権限が、

0111

WRITEが、

0010

なら、ANDを取ります。

0111
0010
----
0010

結果が0ではないので、WRITEのbitが立っています。

JavaScriptなら、

if ((permission & WRITE) !== 0) {
  console.log("書き込み可能");
}

です。

このパターンはビットマスクを使うコードで非常によく出てきます。

なぜANDで確認できるのか

ANDは、

両方が1のときだけ1

になります。

たとえば、

permission = 0111
WRITE      = 0010

なら、WRITEの位置だけを取り出せます。

0111
0010
----
0010

これを「マスクする」と考えると分かりやすいです。

必要なbitだけを残して、ほかを0にしています。

このような値を、

ビットマスク

と呼びます。

権限がない場合

たとえばADMIN権限を確認します。

permission = 0111
ADMIN      = 1000

ANDすると、

0111
1000
----
0000

です。

結果が0なのでADMIN権限はありません。

const hasAdmin = (permission & ADMIN) !== 0;

console.log(hasAdmin);
// false

となります。

権限を外すにはANDとNOTを使う

今度はWRITE権限を削除してみます。

現在、

permission = 0111

です。

WRITEは、

0010

なので、まずNOTで反転します。

4bitだけで考えると、

0010
↓ NOT
1101

です。

これを現在の権限とANDします。

0111
1101
----
0101

結果は、

READ   = ON
WRITE  = OFF
DELETE = ON
ADMIN  = OFF

です。

JavaScriptでは、

permission &= ~WRITE;

と書けます。

ビットフラグではよく使う書き方です。

XORならON/OFFを反転できる

XORは、

同じなら0
違えば1

になる演算です。

これを使うと、特定のフラグだけON/OFFを切り替えられます。

たとえば、

permission ^= WRITE;

とすると、WRITEが、

ONならOFF
OFFならON

になります。

スイッチのような動作です。

XORを実際に見てみる

現在の権限が、

0101

だったとします。

WRITEは、

0010

です。

XORすると、

0101
0010
----
0111

WRITEがONになりました。

もう一度同じXORを実行すると、

0111
0010
----
0101

元に戻ります。

つまり、

permission ^= WRITE;

を実行するたびにWRITEが切り替わります。

まとめると4つの操作になる

ビットフラグを扱う基本操作は、かなりシンプルです。

フラグを追加

flags |= FLAG;

フラグがあるか確認

(flags & FLAG) !== 0

フラグを削除

flags &= ~FLAG;

フラグを反転

flags ^= FLAG;

この4つを理解すると、ビット演算の実用的な使い方がかなり見えてきます。

複数の権限を一度に確認する

READとWRITEの両方が必要な場合を考えます。

まず2つをORでまとめます。

const required = READ | WRITE;

2進数では、

0001
0010
----
0011

です。

ユーザーが両方持っているか確認するなら、

if ((permission & required) === required) {
  console.log("READとWRITEの両方を持っています");
}

とできます。

ここで、

(permission & required) !== 0

としてしまうと、

「どちらか1つでも持っている」

場合にtrueになります。

両方必要なら、

(permission & required) === required

と比較するのがポイントです。

8bitなら8種類の状態を持てる

1bitで、

0
1

の2状態を表せます。

8bitなら、

00000000

から、

11111111

まであります。

それぞれのbitを独立したフラグとして使えば、

bit 0
bit 1
bit 2
bit 3
bit 4
bit 5
bit 6
bit 7

という8種類のON/OFF状態を1つの数値に格納できます。

16bitなら16種類、32bitなら32種類です。

なぜ値が1・2・4・8になるのか

フラグとして、

1
2
4
8
16
32

のような値を見かける理由もここにあります。

2進数では、

1   = 000001
2   = 000010
4   = 000100
8   = 001000
16  = 010000
32  = 100000

となり、それぞれ別のbitだけが1になっています。

つまり、

2の累乗

を使うことで、各フラグが重ならないようにできます。

3や5を単独フラグにしない理由

たとえば、

3 = 0011

です。

すでに2つのbitが立っています。

そのため、

const SOME_FLAG = 3;

のようにすると、独立した1つのフラグとして扱いにくくなります。

一方、

4 = 0100

なら1bitだけなので、ほかのフラグと安全に組み合わせられます。

だからビットフラグでは、

1, 2, 4, 8, 16...

がよく使われます。

16進数で書かれることも多い

ビットマスクは16進数で表現されることもあります。

たとえば、

11111111

は16進数なら、

FF

です。

00001111

なら、

0F

です。

長いビット列になるほど、16進数の方が読みやすくなります。

たとえば、

const MASK = 0xFF;

のようなコードを見かけるのはそのためです。

ビットシフトもフラグ作成に使える

先ほど、

const READ = 1 << 0;
const WRITE = 1 << 1;

と書きました。

左シフトすると、

0001 << 1
↓
0010

になります。

さらに、

0001 << 2
↓
0100

です。

そのため、

const READ   = 1 << 0;
const WRITE  = 1 << 1;
const DELETE = 1 << 2;
const ADMIN  = 1 << 3;

と書くと、

「何番目のbitを使っているか」

がコードから分かりやすくなります。

ビット演算は権限以外でも使われる

ビットフラグの考え方は権限管理だけではありません。

たとえば、

  • ステータス管理
  • オプション設定
  • デバイス制御
  • 通信プロトコル
  • ファイルフォーマット
  • RGBなどの値の抽出
  • 組み込みシステム
  • ゲームの状態管理

などでも使われます。

特にバイナリデータを扱う場面では、

特定のbitだけ取り出したい

という処理がよくあります。

そこでANDやシフトが活躍します。

実際のビット列を確認すると理解しやすい

ビット演算は10進数だけを見ていると直感的に分かりにくいことがあります。

たとえば、

5 AND 3

だけ見るより、

0101
0011
----
0001

と2進数で見た方が、何が起きているか理解しやすくなります。

AND・OR・XORやシフトの結果を2進数で確認したい場合は、ビット列を実際に確認すると、それぞれの演算結果を比較しやすくなります。

JavaScriptのビット演算には注意点もある

JavaScriptで通常のビット演算子を使う場合、数値は基本的に32bit整数として扱われます。

たとえば、

|
&
^
~
<<
>>

などです。

そのため、大きな整数を扱う場合には注意が必要です。

通常のNumberはより大きな整数も表現できますが、ビット演算へ入ると32bit整数へ変換されます。

大きなビット列を扱う実装では、この仕様を理解しておく必要があります。

算術右シフトと論理右シフト

JavaScriptには、

>>

と、

>>>

があります。

>>は符号を維持する右シフトです。

一方、

>>>はゼロを埋める論理右シフトです。

負の値を扱うと結果が違います。

たとえば、

console.log(-8 >> 1);
// -4

一方、

console.log(-8 >>> 1);

では大きな正の整数になります。

これは負数が2の補数で表現されていることと関係しています。

ビット演算を深く理解していくと、

2進数
16進数
2の補数
Signed / Unsigned

といった話がつながってきます。

まとめ

AND・OR・XORは、単なる計算問題として覚えるより、

複数のON/OFF状態を1つの整数で管理する仕組み

として見ると理解しやすくなります。

基本操作は、

OR
→ フラグを追加する
AND
→ フラグを確認する
AND + NOT
→ フラグを削除する
XOR
→ フラグを反転する

です。

さらに、

1
2
4
8
16

のような2の累乗を使えば、それぞれのbitを独立したフラグとして利用できます。

ビット演算は一見すると低レベルで分かりにくい処理ですが、

「どのbitがONになっているかを見る」

という考え方が分かると、権限管理やステータス管理、通信データなど実際のコードでも読みやすくなります。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?