ビット演算子

ビット演算子は、整数値の特定のビットの評価や操作を行います。

ビット演算子
名前 結果
$a & $b ビット積 $a および $b の両方にセットされているビットがセットされます
$a | $b ビット和 $a または $b のどちらかにセットされているビットがセットされます
$a ^ $b 排他的論理和 $a または $b のどちらか一方にセットされており、両方にセットされていないビットがセットされます
~ $a 否定 $a にセットされているビットはセットせず、そうでないものは逆にします
$a << $b 左シフト $a のビットを左に $b ビットシフトする (各シフトは "2をかける" ことを意味します)
$a >> $b 右シフト $a のビットを右に $b ビットシフトします (各シフトは "2で割る" ことを意味します)

PHP のシフト処理は算術シフトです。両端からあふれたビットは捨てられます。 左シフトをすると右側にはゼロが埋められます。 符号ビットは左端からあふれて消えます。 つまり、オペランドの符号は維持されないということです。 右シフトの際には、符号ビットと同じ内容が左端から埋められます。 つまり、この場合はオペランドの符号が維持されます。

括弧を使うことで、望みどおりの 優先順位 で処理させることができます。たとえば、$a & $b == true はまず等価かどうかを評価してからビット演算を行いますが ($a & $b) == true はまずビット演算を行ってから等価かどうかを評価します。

&| そして ^ 演算子の左右のオペランドが文字列の場合、その演算は、 文字列を構成する文字の ASCII 値を使って行います。その結果は文字列になります。 それ以外の場合は、左右のオペランドを integer に変換 し、結果も integer になります。

~ 演算子のオペランドが文字列の場合、その演算は、 文字列を構成する文字の ASCII 値を使って行います。その結果は文字列になります。 それ以外の場合は、オペランドや演算結果を integer として扱います。

<< および >> 演算子のオペランドとその結果は、常に integer として扱います。

PHP の ini 設定 error_reporting はビット値を用いています。
これを用いて、特定のビットを落とす演算の例を見てみましょう。
notice 以外のすべてのエラーを表示させるには、
php.ini ファイルで
E_ALL & ~E_NOTICE
と指定することになります。
    

まずは E_ALL。
00000000000000000111011111111111
そして E_NOTICE...。
00000000000000000000000000001000
... これを ~ で逆転させます。
11111111111111111111111111110111
最後に AND (&) を使い、両方ともビットが立っているところをみつけます。
00000000000000000111011111110111
    

同じ結果を得るもうひとつの方法として、 XOR (^)
を使ってどちらか一方だけ立っているビットを探すという方法もあります。
E_ALL ^ E_NOTICE
    

error_reporting を使って、特定のビットを立てる処理の例を見てみましょう。
通常のエラーとリカバー可能なエラーだけを表示させるには、次のようにします。
E_ERROR | E_RECOVERABLE_ERROR
    

この処理は、 E_ERROR
0000000000000000000000000000000100000000000000000001000000000000
を OR (|) 演算子でつないで、
少なくともどちらかのビットが立っているところを取得します。
00000000000000000001000000000001
    

例1 整数値におけるビット AND、OR および XOR 演算

<?php
/*
 * このへんは無視してください。
 * たんに結果をきれいに表示させるためだけのものです。
 */

$format = '(%1$2d = %1$04b) = (%2$2d = %2$04b)'
      . ' %3$s (%4$2d = %4$04b)' . "\n";

echo <<<EOH
 ---------     ---------  -- ---------
 result        value      op test
 ---------     ---------  -- ---------
EOH;


/*
 * ここからが本番
 */

$values = array(0, 1, 2, 4, 8);
$test = 1 + 4;

echo "\n Bitwise AND \n";
foreach ($values as $value) {
  $result = $value & $test;
  printf($format, $result, $value, '&', $test);
}

echo "\n Bitwise Inclusive OR \n";
foreach ($values as $value) {
  $result = $value | $test;
  printf($format, $result, $value, '|', $test);
}

echo "\n Bitwise Exclusive OR (XOR) \n";
foreach ($values as $value) {
  $result = $value ^ $test;
  printf($format, $result, $value, '^', $test);
}
?>

上の例の出力は以下となります。

 ---------     ---------  -- ---------
 result        value      op test
 ---------     ---------  -- ---------
 Bitwise AND
( 0 = 0000) = ( 0 = 0000) & ( 5 = 0101)
( 1 = 0001) = ( 1 = 0001) & ( 5 = 0101)
( 0 = 0000) = ( 2 = 0010) & ( 5 = 0101)
( 4 = 0100) = ( 4 = 0100) & ( 5 = 0101)
( 0 = 0000) = ( 8 = 1000) & ( 5 = 0101)

 Bitwise Inclusive OR
( 5 = 0101) = ( 0 = 0000) | ( 5 = 0101)
( 5 = 0101) = ( 1 = 0001) | ( 5 = 0101)
( 7 = 0111) = ( 2 = 0010) | ( 5 = 0101)
( 5 = 0101) = ( 4 = 0100) | ( 5 = 0101)
(13 = 1101) = ( 8 = 1000) | ( 5 = 0101)

 Bitwise Exclusive OR (XOR)
( 5 = 0101) = ( 0 = 0000) ^ ( 5 = 0101)
( 4 = 0100) = ( 1 = 0001) ^ ( 5 = 0101)
( 7 = 0111) = ( 2 = 0010) ^ ( 5 = 0101)
( 1 = 0001) = ( 4 = 0100) ^ ( 5 = 0101)
(13 = 1101) = ( 8 = 1000) ^ ( 5 = 0101)

例2 文字列でのビット XOR 演算

<?php
echo 12 ^ 9; // 出力は '5'

echo "12" ^ "9"; // 出力はバックスペース文字 (ascii 8)
               // ('1' (ascii 49)) ^ ('9' (ascii 57)) = #8

echo "hallo" ^ "hello"; // 出力は、ascii コード #0 #4 #0 #0 #0
                      // 'a' ^ 'e' = #4

echo 2 ^ "3"; // 出力は 1
              // 2 ^ ((int) "3") == 1

echo "2" ^ 3; // 出力は 1
              // ((int) "2") ^ 3 == 1
?>

例3 整数値のビットシフト

<?php
/*
 * これが例です
 */

echo "\n--- BIT SHIFT RIGHT ON POSITIVE INTEGERS ---\n";

$val = 4;
$places = 1;
$res = $val >> $places;
p($res, $val, '>>', $places, 'copy of sign bit shifted into left side');

$val = 4;
$places = 2;
$res = $val >> $places;
p($res, $val, '>>', $places);

$val = 4;
$places = 3;
$res = $val >> $places;
p($res, $val, '>>', $places, 'bits shift out right side');

$val = 4;
$places = 4;
$res = $val >> $places;
p($res, $val, '>>', $places, 'same result as above; can not shift beyond 0');


echo "\n--- BIT SHIFT RIGHT ON NEGATIVE INTEGERS ---\n";

$val = -4;
$places = 1;
$res = $val >> $places;
p($res, $val, '>>', $places, 'copy of sign bit shifted into left side');

$val = -4;
$places = 2;
$res = $val >> $places;
p($res, $val, '>>', $places, 'bits shift out right side');

$val = -4;
$places = 3;
$res = $val >> $places;
p($res, $val, '>>', $places, 'same result as above; can not shift beyond -1');


echo "\n--- BIT SHIFT LEFT ON POSITIVE INTEGERS ---\n";

$val = 4;
$places = 1;
$res = $val << $places;
p($res, $val, '<<', $places, 'zeros fill in right side');

$val = 4;
$places = (PHP_INT_SIZE * 8) - 4;
$res = $val << $places;
p($res, $val, '<<', $places);

$val = 4;
$places = (PHP_INT_SIZE * 8) - 3;
$res = $val << $places;
p($res, $val, '<<', $places, 'sign bits get shifted out');

$val = 4;
$places = (PHP_INT_SIZE * 8) - 2;
$res = $val << $places;
p($res, $val, '<<', $places, 'bits shift out left side');


echo "\n--- BIT SHIFT LEFT ON NEGATIVE INTEGERS ---\n";

$val = -4;
$places = 1;
$res = $val << $places;
p($res, $val, '<<', $places, 'zeros fill in right side');

$val = -4;
$places = (PHP_INT_SIZE * 8) - 3;
$res = $val << $places;
p($res, $val, '<<', $places);

$val = -4;
$places = (PHP_INT_SIZE * 8) - 2;
$res = $val << $places;
p($res, $val, '<<', $places, 'bits shift out left side, including sign bit');


/*
 * このへんは無視してください。
 * たんに結果をきれいに表示させるためだけのものです。
 */

function p($res, $val, $op, $places, $note = '') {
  $format = '%0' . (PHP_INT_SIZE * 8) . "b\n";

  printf("Expression: %d = %d %s %d\n", $res, $val, $op, $places);

  echo " Decimal:\n";
  printf("  val=%d\n", $val);
  printf("  res=%d\n", $res);

  echo " Binary:\n";
  printf('  val=' . $format, $val);
  printf('  res=' . $format, $res);

  if ($note) {
      echo " NOTE: $note\n";
  }

  echo "\n";
}
?>

上の例の 32 ビットマシンでの出力は、このようになります。


--- BIT SHIFT RIGHT ON POSITIVE INTEGERS ---
Expression: 2 = 4 >> 1
 Decimal:
val=4
res=2
 Binary:
val=00000000000000000000000000000100
res=00000000000000000000000000000010
 NOTE: copy of sign bit shifted into left side

Expression: 1 = 4 >> 2
 Decimal:
val=4
res=1
 Binary:
val=00000000000000000000000000000100
res=00000000000000000000000000000001

Expression: 0 = 4 >> 3
 Decimal:
val=4
res=0
 Binary:
val=00000000000000000000000000000100
res=00000000000000000000000000000000
 NOTE: bits shift out right side

Expression: 0 = 4 >> 4
 Decimal:
val=4
res=0
 Binary:
val=00000000000000000000000000000100
res=00000000000000000000000000000000
 NOTE: same result as above; can not shift beyond 0


--- BIT SHIFT RIGHT ON NEGATIVE INTEGERS ---
Expression: -2 = -4 >> 1
 Decimal:
val=-4
res=-2
 Binary:
val=11111111111111111111111111111100
res=11111111111111111111111111111110
 NOTE: copy of sign bit shifted into left side

Expression: -1 = -4 >> 2
 Decimal:
val=-4
res=-1
 Binary:
val=11111111111111111111111111111100
res=11111111111111111111111111111111
 NOTE: bits shift out right side

Expression: -1 = -4 >> 3
 Decimal:
val=-4
res=-1
 Binary:
val=11111111111111111111111111111100
res=11111111111111111111111111111111
 NOTE: same result as above; can not shift beyond -1


--- BIT SHIFT LEFT ON POSITIVE INTEGERS ---
Expression: 8 = 4 << 1
 Decimal:
val=4
res=8
 Binary:
val=00000000000000000000000000000100
res=00000000000000000000000000001000
 NOTE: zeros fill in right side

Expression: 1073741824 = 4 << 28
 Decimal:
val=4
res=1073741824
 Binary:
val=00000000000000000000000000000100
res=01000000000000000000000000000000

Expression: -2147483648 = 4 << 29
 Decimal:
val=4
res=-2147483648
 Binary:
val=00000000000000000000000000000100
res=10000000000000000000000000000000
 NOTE: sign bits get shifted out

Expression: 0 = 4 << 30
 Decimal:
val=4
res=0
 Binary:
val=00000000000000000000000000000100
res=00000000000000000000000000000000
 NOTE: bits shift out left side


--- BIT SHIFT LEFT ON NEGATIVE INTEGERS ---
Expression: -8 = -4 << 1
 Decimal:
val=-4
res=-8
 Binary:
val=11111111111111111111111111111100
res=11111111111111111111111111111000
 NOTE: zeros fill in right side

Expression: -2147483648 = -4 << 29
 Decimal:
val=-4
res=-2147483648
 Binary:
val=11111111111111111111111111111100
res=10000000000000000000000000000000

Expression: 0 = -4 << 30
 Decimal:
val=-4
res=0
 Binary:
val=11111111111111111111111111111100
res=00000000000000000000000000000000
 NOTE: bits shift out left side, including sign bit

上の例の 64 ビットマシンでの出力は、このようになります。


--- BIT SHIFT RIGHT ON POSITIVE INTEGERS ---
Expression: 2 = 4 >> 1
 Decimal:
val=4
res=2
 Binary:
val=0000000000000000000000000000000000000000000000000000000000000100
res=0000000000000000000000000000000000000000000000000000000000000010
 NOTE: copy of sign bit shifted into left side

Expression: 1 = 4 >> 2
 Decimal:
val=4
res=1
 Binary:
val=0000000000000000000000000000000000000000000000000000000000000100
res=0000000000000000000000000000000000000000000000000000000000000001

Expression: 0 = 4 >> 3
 Decimal:
val=4
res=0
 Binary:
val=0000000000000000000000000000000000000000000000000000000000000100
res=0000000000000000000000000000000000000000000000000000000000000000
 NOTE: bits shift out right side

Expression: 0 = 4 >> 4
 Decimal:
val=4
res=0
 Binary:
val=0000000000000000000000000000000000000000000000000000000000000100
res=0000000000000000000000000000000000000000000000000000000000000000
 NOTE: same result as above; can not shift beyond 0


--- BIT SHIFT RIGHT ON NEGATIVE INTEGERS ---
Expression: -2 = -4 >> 1
 Decimal:
val=-4
res=-2
 Binary:
val=1111111111111111111111111111111111111111111111111111111111111100
res=1111111111111111111111111111111111111111111111111111111111111110
 NOTE: copy of sign bit shifted into left side

Expression: -1 = -4 >> 2
 Decimal:
val=-4
res=-1
 Binary:
val=1111111111111111111111111111111111111111111111111111111111111100
res=1111111111111111111111111111111111111111111111111111111111111111
 NOTE: bits shift out right side

Expression: -1 = -4 >> 3
 Decimal:
val=-4
res=-1
 Binary:
val=1111111111111111111111111111111111111111111111111111111111111100
res=1111111111111111111111111111111111111111111111111111111111111111
 NOTE: same result as above; can not shift beyond -1


--- BIT SHIFT LEFT ON POSITIVE INTEGERS ---
Expression: 8 = 4 << 1
 Decimal:
val=4
res=8
 Binary:
val=0000000000000000000000000000000000000000000000000000000000000100
res=0000000000000000000000000000000000000000000000000000000000001000
 NOTE: zeros fill in right side

Expression: 4611686018427387904 = 4 << 60
 Decimal:
val=4
res=4611686018427387904
 Binary:
val=0000000000000000000000000000000000000000000000000000000000000100
res=0100000000000000000000000000000000000000000000000000000000000000

Expression: -9223372036854775808 = 4 << 61
 Decimal:
val=4
res=-9223372036854775808
 Binary:
val=0000000000000000000000000000000000000000000000000000000000000100
res=1000000000000000000000000000000000000000000000000000000000000000
 NOTE: sign bits get shifted out

Expression: 0 = 4 << 62
 Decimal:
val=4
res=0
 Binary:
val=0000000000000000000000000000000000000000000000000000000000000100
res=0000000000000000000000000000000000000000000000000000000000000000
 NOTE: bits shift out left side


--- BIT SHIFT LEFT ON NEGATIVE INTEGERS ---
Expression: -8 = -4 << 1
 Decimal:
val=-4
res=-8
 Binary:
val=1111111111111111111111111111111111111111111111111111111111111100
res=1111111111111111111111111111111111111111111111111111111111111000
 NOTE: zeros fill in right side

Expression: -9223372036854775808 = -4 << 61
 Decimal:
val=-4
res=-9223372036854775808
 Binary:
val=1111111111111111111111111111111111111111111111111111111111111100
res=1000000000000000000000000000000000000000000000000000000000000000

Expression: 0 = -4 << 62
 Decimal:
val=-4
res=0
 Binary:
val=1111111111111111111111111111111111111111111111111111111111111100
res=0000000000000000000000000000000000000000000000000000000000000000
 NOTE: bits shift out left side, including sign bit

警告

PHP_INT_MAX を超える数のビット演算には、 gmp 拡張モジュールの関数を使いましょう。

参考

  • pack
  • unpack
  • gmp_and
  • gmp_or
  • gmp_xor
  • gmp_testbit
  • gmp_clrbit