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

### [目次に戻る](http://qiita.com/mpyw/items/752e19a578cf7d96fc5f)

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

# ビット演算の概要

## 導入

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

### 【例】 $3 + 5 = 8$

```php:10進数での表記
$a = 3;
$b = 5;
echo $a + $b;
```

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

```php:2進数での表記(PHP5.4以降のみ)
$a = 0b11;            //  011 (3)
$b = 0b101;           // +101 (5)
                      // --------
echo decbin($a + $b); // 1000 (8)
```

2進数は<ins>2で繰り上がる</ins>ので、上記のような結果が得られます。さあ、ここから2進数の各桁に注目してみましょう。なお、負の数の扱いは少し複雑なので、ここでは正の数だけを扱うことにします。

## ビット積 `&`

- どちらも `1` ならば `1`
- どちらかが `0` ならば `0`

### 【例】 $3 \mbox{&} 5 = 1$

```php
$a = 0b11;            //  011 (3)
$b = 0b101;           // &101 (5)
                      // --------
echo decbin($a & $b); //  001 (1)
```

## ビット和 `|`

- どちらかが `1` ならば `1`
- どちらも `0` ならば `0`

### 【例】 $3 \mbox{|} 5 = 7$

```php
$a = 0b11;            //  011 (3)
$b = 0b101;           // |101 (5)
                      // --------
echo decbin($a | $b); //  111 (7)
```

## 排他的論理和 `^`

- どちらか<ins>一方のみ</ins>が `1` ならば `1`
- どちらも `0` ならば `0`
- どちらも `1` ならば `0`

### 【例】 $3 \mbox{^} 5 = 6$

```php
$a = 0b11;            //  011 (3)
$b = 0b101;           // ^101 (5)
                      // --------
echo decbin($a ^ $b); //  110 (6)
```

## 否定 `~`

- `0` ならば `1`
- `1` ならば `0`

### 【例】 $\mbox{~}3 = -4$

```php:32ビット環境での例
$a = 0b11;            // ~00000000000000000000000000000011 (3)
                      // --------------------------------------
echo decbin(~$a);     //  11111111111111111111111111111100 (-4)
```

最上位の符号ビットも反転するため `-4` になりますが、あまり深く悩む必要は無いでしょう。それよりも次に説明するビットフラグの扱いの方が重要です。

## 左シフト `<<`

- 指定した分だけ桁を右にずらす
- 空いたところには `0` が埋められる
- 10進数の<ins>2をかける</ins>演算と同じになる

### 【例】 $3 \mbox{<<} 1 = 6$

```php
$a = 0b11;            // <<011 (3)
                      // --------
echo decbin($a << 1); //   110 (6)
```

## 右シフト `>>`

- 指定した分だけ桁を右にずらす
- 空いたところには `0` が埋められる
- 10進数の<ins>2で割る</ins>演算と同じになる

### 【例】 $3 \mbox{>>} 1 = 1$

```php
$a = 0b11;            // >>011 (3)
                      // --------
echo decbin($a >> 1); //   001 (1)
```

# なぜビット演算が必要か？

「なんでこんな面倒なことしなくちゃいけないの？」って多くの初心者さんが感じていることと思います。ビット演算の一番重要な目的は、 **フラグの受け渡し** です。

例えば、下記のような関数を実装するとします。

```php
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` と出力したければ、

```php
display(false, true, true, false);
```

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

```php
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';
    }
}
```

```php
display(DISP_B | DISP_C);
```

`DISP_A` `DISP_B` `DISP_C` `DISP_D` という4つの定数を用意しました。この `1` `2` `4` `8` という値は、コメントを見てもらえば分かると思いますが、<ins>その番目の桁だけが `1`</ins> となるように構成されています。1つの桁に1種類の情報を格納しよう、というのが狙いです。

順に流れを追っていきます。

> ```php
> display(DISP_B | DISP_C);
> ```

これで `0b0110` がフラグとして渡されます。

> ```php
> if (DISP_A & $flag) {
>     echo 'a';
> }
> ```

ここは `0b0001 & 0b0110` の演算が発生し、結果は `0b0000` 、つまり `0` になります。`0` は論理値 `FALSE` として変換されるので、この `echo` は実行されません。

> ```php
> if (DISP_B & $flag) {
>     echo 'b';
> }
> ```

ここは `0b0010 & 0b0110` の演算が発生し、結果は `0b0010` 、つまり `2` になります。`2` は論理値 `TRUE` として変換されるので、この `echo` は実行されます。

> ```php
> if (DISP_C & $flag) {
>     echo 'c';
> }
> ```

ここは `0b0100 & 0b0110` の演算が発生し、結果は `0b0100` 、つまり `4` になります。`4` は論理値 `TRUE` として変換されるので、この `echo` は実行されます。

> ```php
> if (DISP_D & $flag) {
>     echo 'd';
> }
> ```

ここは `0b1000 & 0b0110` の演算が発生し、結果は `0b0000` 、つまり `0` になります。`0` は論理値 `FALSE` として変換されるので、この `echo` は実行されません。

### ビット演算のメリット

- 引数を1つに出来る
- 必要なものだけの **ビット和** をとって渡せばいいようになる
- 関数側で1つの値から **ビット積** を使って複数の情報を取り出せる