Edited at

【PHP入門講座】 ビット演算

More than 5 years have passed since last update.


目次に戻る

ビット演算は、PHPを扱う上に置いてはそれほど重要性は高くないのですが、次の「演算子」の説明で必要性が生じてくるので、ここであらかじめ触れておきます。


ビット演算の概要


導入

2進数演算を考えましょう。


【例】 $3 + 5 = 8$


10進数での表記

$a = 3;

$b = 5;
echo $a + $b;

整数を2進数で表記するためには 0b を頭につける、と既に説明済みですが、結局データとしては10進数として扱われてしまうので、2進数として出力したい場合には decbin base_convert sprintf などの関数を使って2進数表記の文字列に変換する必要があります。ここでは一番シンプルな decbin 関数を利用します。


2進数での表記(PHP5.4以降のみ)

$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$


32ビット環境での例

$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);

としますが、もしこれが az まであったらどうでしょうか?面倒くさいってレベルを超えているでしょう。そこでビット演算の出番です。このように書き換えることが出来ます。

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つの値から ビット積 を使って複数の情報を取り出せる