目次に戻る
ビット演算は、PHPを扱う上に置いてはそれほど重要性は高くないのですが、次の「演算子」の説明で必要性が生じてくるので、ここであらかじめ触れておきます。
ビット演算の概要
導入
2進数演算を考えましょう。
【例】 $3 + 5 = 8$
$a = 3;
$b = 5;
echo $a + $b;
整数を2進数で表記するためには 0b
を頭につける、と既に説明済みですが、結局データとしては10進数として扱われてしまうので、2進数として出力したい場合には decbin
base_convert
sprintf
などの関数を使って2進数表記の文字列に変換する必要があります。ここでは一番シンプルな decbin
関数を利用します。
$a = 0b11; // 011 (3)
$b = 0b101; // +101 (5)
// --------
echo decbin($a + $b); // 1000 (8)
2進数は2で繰り上がるので、上記のような結果が得られます。さあ、ここから2進数の各桁に注目してみましょう。なお、負の数の扱いは少し複雑なので、ここでは正の数だけを扱うことにします。
ビット積 &
- どちらも
1
ならば1
- どちらかが
0
ならば0
【例】 $3 \mbox{&} 5 = 1$
$a = 0b11; // 011 (3)
$b = 0b101; // &101 (5)
// --------
echo decbin($a & $b); // 001 (1)
ビット和 |
- どちらかが
1
ならば1
- どちらも
0
ならば0
【例】 $3 \mbox{|} 5 = 7$
$a = 0b11; // 011 (3)
$b = 0b101; // |101 (5)
// --------
echo decbin($a | $b); // 111 (7)
排他的論理和 ^
- どちらか一方のみが
1
ならば1
- どちらも
0
ならば0
- どちらも
1
ならば0
【例】 $3 \mbox{^} 5 = 6$
$a = 0b11; // 011 (3)
$b = 0b101; // ^101 (5)
// --------
echo decbin($a ^ $b); // 110 (6)
否定 ~
-
0
ならば1
-
1
ならば0
【例】 $\mbox{~}3 = -4$
$a = 0b11; // ~00000000000000000000000000000011 (3)
// --------------------------------------
echo decbin(~$a); // 11111111111111111111111111111100 (-4)
最上位の符号ビットも反転するため -4
になりますが、あまり深く悩む必要は無いでしょう。それよりも次に説明するビットフラグの扱いの方が重要です。
左シフト <<
- 指定した分だけ桁を右にずらす
- 空いたところには
0
が埋められる - 10進数の2をかける演算と同じになる
【例】 $3 \mbox{<<} 1 = 6$
$a = 0b11; // <<011 (3)
// --------
echo decbin($a << 1); // 110 (6)
右シフト >>
- 指定した分だけ桁を右にずらす
- 空いたところには
0
が埋められる - 10進数の2で割る演算と同じになる
【例】 $3 \mbox{>>} 1 = 1$
$a = 0b11; // >>011 (3)
// --------
echo decbin($a >> 1); // 001 (1)
なぜビット演算が必要か?
「なんでこんな面倒なことしなくちゃいけないの?」って多くの初心者さんが感じていることと思います。ビット演算の一番重要な目的は、 フラグの受け渡し です。
例えば、下記のような関数を実装するとします。
function display($disp_a, $disp_b, $disp_c, $disp_d) {
if ($disp_a) {
echo 'a';
}
if ($disp_b) {
echo 'b';
}
if ($disp_c) {
echo 'c';
}
if ($disp_d) {
echo 'd';
}
}
ここで bc
と出力したければ、
display(false, true, true, false);
としますが、もしこれが a
~ z
まであったらどうでしょうか?面倒くさいってレベルを超えているでしょう。そこでビット演算の出番です。このように書き換えることが出来ます。
const DISP_A = 1; // 0b0001
const DISP_B = 2; // 0b0010
const DISP_C = 4; // 0b0100
const DISP_D = 8; // 0b1000
function display($flag) {
if (DISP_A & $flag) {
echo 'a';
}
if (DISP_B & $flag) {
echo 'b';
}
if (DISP_C & $flag) {
echo 'c';
}
if (DISP_D & $flag) {
echo 'd';
}
}
display(DISP_B | DISP_C);
DISP_A
DISP_B
DISP_C
DISP_D
という4つの定数を用意しました。この 1
2
4
8
という値は、コメントを見てもらえば分かると思いますが、その番目の桁だけが 1
となるように構成されています。1つの桁に1種類の情報を格納しよう、というのが狙いです。
順に流れを追っていきます。
display(DISP_B | DISP_C);
これで 0b0110
がフラグとして渡されます。
if (DISP_A & $flag) { echo 'a'; }
ここは 0b0001 & 0b0110
の演算が発生し、結果は 0b0000
、つまり 0
になります。0
は論理値 FALSE
として変換されるので、この echo
は実行されません。
if (DISP_B & $flag) { echo 'b'; }
ここは 0b0010 & 0b0110
の演算が発生し、結果は 0b0010
、つまり 2
になります。2
は論理値 TRUE
として変換されるので、この echo
は実行されます。
if (DISP_C & $flag) { echo 'c'; }
ここは 0b0100 & 0b0110
の演算が発生し、結果は 0b0100
、つまり 4
になります。4
は論理値 TRUE
として変換されるので、この echo
は実行されます。
if (DISP_D & $flag) { echo 'd'; }
ここは 0b1000 & 0b0110
の演算が発生し、結果は 0b0000
、つまり 0
になります。0
は論理値 FALSE
として変換されるので、この echo
は実行されません。
ビット演算のメリット
- 引数を1つに出来る
- 必要なものだけの ビット和 をとって渡せばいいようになる
- 関数側で1つの値から ビット積 を使って複数の情報を取り出せる