Bitwise Operators
suggest changePrefix bitwise operators
Bitwise operators are like logical operators but executed per bit rather than per boolean value.
// bitwise NOT ~: sets all unset bits and unsets all set bits
printf("%'06b", ~0b110110); // 001001
Bitmask-bitmask operators
Bitwise AND &
: a bit is set only if it is set in both operands
printf("%'06b", 0b110101 & 0b011001); // 010001
Bitwise OR |
: a bit is set if it is set in either or both operands
printf("%'06b", 0b110101 | 0b011001); // 111101
Bitwise XOR ^
: a bit is set if it is set in one operand and not set in another operand, i.e. only if that bit is in different state in the two operands
printf("%'06b", 0b110101 ^ 0b011001); // 101100
Example uses of bitmasks
These operators can be used to manipulate bitmasks. For example:
file_put_contents("file.log", LOCK_EX | FILE_APPEND);
Here, the |
operator is used to combine the two bitmasks. Although \+
has the same effect, |
emphasizes that you are combining bitmasks, not adding two normal scalar integers.
class Foo{
const OPTION_A = 1;
const OPTION_B = 2;
const OPTION_C = 4;
const OPTION_A = 8;
private $options = self::OPTION_A | self::OPTION_C;
public function toggleOption(int $option){
$this->options ^= $option;
}
public function enable(int $option){
$this->options |= $option; // enable $option regardless of its original state
}
public function disable(int $option){
$this->options &= ~$option; // disable $option regardless of its original state,
// without affecting other bits
}
/** returns whether at least one of the options is enabled */
public function isOneEnabled(int $options) : bool{
return $this->options & $option !== 0;
// Use !== rather than >, because
// if $options is about a high bit, we may be handling a negative integer
}
/** returns whether all of the options are enabled */
public function areAllEnabled(int $options) : bool{
return ($this->options & $options) === $options;
// note the parentheses; beware the operator precedence
}
}
This example (assuming $option
always only contain one bit) uses:
- the
^
operator to conveniently toggle bitmasks. - the
|
operator to set a bit neglecting its original state or other bits - the
~
operator to convert an integer with only one bit set into an integer with only one bit not set - the
&
operator to unset a bit, using these properties of&
: - Since
&=
with a set bit will not do anything ((1 & 1) === 1
,(0 & 1) === 0
), doing&=
with an integer with only one bit not set will only unset that bit, not affecting other bits. &=
with an unset bit will unset that bit ((1 & 0) === 0
,(0 & 0) === 0
)- Using the
&
operator with another bitmask will filter away all other bits not set in that bitmask. - If the output has any bits set, it means that any one of the options are enabled.
- If the output has all bits of the bitmask set, it means that all of the options in the bitmask are enabled.
Bear in mind that these comparison operators: (\<
\>
<=
>=
==
===
!=
!==
<>
<=>
) have higher precedence than these bitmask-bitmask operators: (|
^
&
). As bitwise results are often compared using these comparison operators, this is a common pitfall to be aware of.
Bit-shifting operators
Bitwise left shift <<: shift all bits to the left (more significant) by the given number of steps and discard the bits exceeding the int size
<< $x
is equivalent to unsetting the highest $x
bits and multiplying by the $x
th power of 2
printf("%'08b", 0b00001011<< 2); // 00101100
assert(PHP_INT_SIZE === 4); // a 32-bit system
printf("%x, %x", 0x5FFFFFFF << 2, 0x1FFFFFFF << 4); // 7FFFFFFC, FFFFFFFF
Bitwise right shift >>
: discard the lowest shift and shift the remaining bits to the right (less significant)
>> $x
is equivalent to dividing by the $x
th power of 2 and discard the non-integer part
printf("%x", 0xFFFFFFFF >> 3); // 1FFFFFFF
Example uses of bit shifting:
Fast division by 16 (better performance than /= 16
)
$x >>= 4;
On 32-bit systems, this discards all bits in the integer, setting the value to 0. On 64-bit systems, this unsets the most significant 32 bits and keep the least
$x = $x << 32 >> 32;
significant 32 bits, equivalent to $x & 0xFFFFFFFF
Note: In this example, printf("%'06b")
is used. It outputs the value in 6 binary digits.