비트 연산자
비트 연산자
정수를 비트 단위로 다루는 비트 연산자들을 살펴봐요. &, |, ^, ~, <<, >> 연산자가 어떻게 동작하는지 다룹니다.
본문
비트 연산자는 정수 안의 특정 비트들을 평가하고 조작할 수 있게 해줘요.
비트 연산자 (Bitwise Operators)
| 예제 | 이름 | 결과 |
|---|---|---|
$a & $b |
And | $a와 $b 양쪽에서 켜진 비트가 켜집니다. |
| `$a | $b` | Or (배타적이지 않은 or) |
$a ^ $b |
Xor (배타적 or) | $a나 $b 중 한쪽에서만 켜진 비트가 켜집니다. |
~ $a |
Not | $a에서 켜진 비트는 꺼지고, 그 반대도 마찬가지입니다. |
$a << $b |
왼쪽 시프트 | $a의 비트를 왼쪽으로 $b 단계 이동(각 단계는 "2로 곱하기"). |
$a >> $b |
오른쪽 시프트 | $a의 비트를 오른쪽으로 $b 단계 이동(각 단계는 "2로 나누기"). |
PHP에서 비트 시프트는 산술적(arithmetic)이에요. 어느 한쪽 끝에서 빠져나간 비트는 버려집니다. 왼쪽 시프트는 오른쪽에 0이 채워지면서 왼쪽으로는 부호 비트가 빠져나가요. 즉 피연산자의 부호는 보존되지 않습니다. 오른쪽 시프트는 왼쪽에 부호 비트의 복사본이 채워져서 피연산자의 부호가 보존돼요.
원하는 우선순위를 보장하려면 괄호를 사용하세요. 예를 들어 $a & $b == true는 동등 비교를 먼저 평가한 뒤 비트 AND를 수행하고, ($a & $b) == true는 비트 AND를 먼저 평가한 뒤 동등 비교를 수행해요.
&, |, ^ 연산자의 두 피연산자가 모두 문자열이면, 문자열을 이루는 문자의 ASCII 값에 대해 연산이 수행되고 결과도 문자열이 돼요. 그 외의 모든 경우에는 두 피연산자 모두 정수로 변환되고 결과도 정수입니다.
~ 연산자의 피연산자가 문자열이면 문자열을 이루는 문자의 ASCII 값에 대해 연산이 수행되고 결과도 문자열이에요. 그렇지 않으면 피연산자와 결과 모두 정수로 취급됩니다.
<<와 >> 연산자의 두 피연산자와 결과는 항상 정수로 취급돼요.
PHP의 error_reporting ini 설정은 비트 값을 사용하는데, 비트를 끄는 동작의 실제 사례를 보여줘요. 공지(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
00000000000000000000000000000001
와
00000000000000000001000000000000
를 OR(|) 연산자로 결합해 어느 한 값에서든 켜진 비트를 얻습니다.
00000000000000000001000000000001
<?php
/*
* Ignore the top section,
* it is just formatting to make output clearer.
*/
$format = '(%1$2d = %1$04b) = (%2$2d = %2$04b)'
. ' %3$s (%4$2d = %4$04b)' . "\n";
echo <<<EOH
--------- --------- -- ---------
result value op test
--------- --------- -- ---------
EOH;
/*
* Here are the examples.
*/
$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)
<?php
echo 12 ^ 9, PHP_EOL; // Outputs '5'
echo "12" ^ "9", PHP_EOL; // Outputs the Backspace character (ascii 8)
// ('1' (ascii 49)) ^ ('9' (ascii 57)) = #8
echo "hallo" ^ "hello", PHP_EOL; // Outputs the ascii values #0 #4 #0 #0 #0
// 'a' ^ 'e' = #4
echo 2 ^ "3", PHP_EOL; // Outputs 1
// 2 ^ ((int) "3") == 1
echo "2" ^ 3, PHP_EOL; // Outputs 1
// ((int) "2") ^ 3 == 1
?>
<?php
/*
* Here are the examples.
*/
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');
/*
* Ignore this bottom section,
* it is just formatting to make output clearer.
*/
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\n";
}
?>
--- 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
--- 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()