Help us understand the problem. What is going on with this article?

【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つの値から ビット積 を使って複数の情報を取り出せる
mpyw
古い記事はそのまま参考にしないようにご注意ください
synapse
Synapseは、オンラインサロンサービスにおけるパイオニアとして、かつて存在していたスタートアップです。
https://synapseam.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away