©
本文檔使用 php中文網手册 發布
位运算符允许对整型数中指定的位进行求值和操作。
例子 | 名称 | 结果 |
---|---|---|
$a & $b | And(按位与) | 将把 $a 和 $b 中都为 1 的位设为 1。 |
$a | $b | Or(按位或) | 将把 $a 和 $b 中任何一个为 1 的位设为 1。 |
$a ^ $b | Xor(按位异或) | 将把 $a 和 $b 中一个为 1 另一个为 0 的位设为 1。 |
~ $a | Not(按位取反) | 将 $a 中为 0 的位设为 1,反之亦然。 |
$a << $b | Shift left(左移) | 将 $a 中的位向左移动 $b 次(每一次移动都表示“乘以 2”)。 |
$a >> $b | Shift right(右移) | 将 $a 中的位向右移动 $b 次(每一次移动都表示“除以 2”)。 |
位移在 PHP 中是数学运算。向任何方向移出去的位都被丢弃。左移时右侧以零填充,符号位被移走意味着正负号不被保留。右移时左侧以符号位填充,意味着正负号被保留。
要用括号确保想要的优先级。例如 $a & $b == true 先进行比较再进行按位与;而 ($a & $b) == true 则先进行按位与再进行比较。
要注意数据类型的转换。如果左右参数都是字符串,则位运算符将对字符的 ASCII 值进行操作。
PHP 的 ini 设定 error_reporting 使用了按位的值,
提供了关闭某个位的真实例子。要显示除了提示级别
之外的所有错误,php.ini 中是这样用的:
E_ALL & ~E_NOTICE
具体运作方式是先取得 E_ALL 的值: 00000000000000000111011111111111 再取得 E_NOTICE 的值: 00000000000000000000000000001000 然后通过 ~ 将其取反: 11111111111111111111111111110111 最后再用按位与 AND(&)得到两个值中都设定了(为 1)的位: 00000000000000000111011111110111
另外一个方法是用按位异或 XOR(^)来取得只在
其中一个值中设定了的位:
E_ALL ^ E_NOTICE
error_reporting 也可用来演示怎样置位。只显示错误和可恢复
错误的方法是:
E_ERROR | E_RECOVERABLE_ERROR
也就是将 E_ERROR 00000000000000000000000000000001 和 E_RECOVERABLE_ERROR 00000000000000000001000000000000 用按位或 OR(|)运算符来取得在任何一个值中被置位的结果: 00000000000000000001000000000001
Example #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)
Example #2 字符串的 XOR 运算符
<?php
echo 12 ^ 9 ; // Outputs '5'
echo "12" ^ "9" ; // Outputs the Backspace character (ascii 8)
// ('1' (ascii 49)) ^ ('9' (ascii 57)) = #8
echo "hallo" ^ "hello" ; // Outputs the ascii values #0 #4 #0 #0 #0
// 'a' ^ 'e' = #4
echo 2 ^ "3" ; // Outputs 1
// 2 ^ ((int)"3") == 1
echo "2" ^ 3 ; // Outputs 1
// ((int)"2") ^ 3 == 1
?>
Example #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 sideExpression: 1 = 4 >> 2 Decimal: val=4 res=1 Binary: val=00000000000000000000000000000100 res=00000000000000000000000000000001Expression: 0 = 4 >> 3 Decimal: val=4 res=0 Binary: val=00000000000000000000000000000100 res=00000000000000000000000000000000 NOTE: bits shift out right sideExpression: 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 sideExpression: -1 = -4 >> 2 Decimal: val=-4 res=-1 Binary: val=11111111111111111111111111111100 res=11111111111111111111111111111111 NOTE: bits shift out right sideExpression: -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 sideExpression: 1073741824 = 4 << 28 Decimal: val=4 res=1073741824 Binary: val=00000000000000000000000000000100 res=01000000000000000000000000000000Expression: -2147483648 = 4 << 29 Decimal: val=4 res=-2147483648 Binary: val=00000000000000000000000000000100 res=10000000000000000000000000000000 NOTE: sign bits get shifted outExpression: 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 sideExpression: -2147483648 = -4 << 29 Decimal: val=-4 res=-2147483648 Binary: val=11111111111111111111111111111100 res=10000000000000000000000000000000Expression: 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 sideExpression: 1 = 4 >> 2 Decimal: val=4 res=1 Binary: val=0000000000000000000000000000000000000000000000000000000000000100 res=0000000000000000000000000000000000000000000000000000000000000001Expression: 0 = 4 >> 3 Decimal: val=4 res=0 Binary: val=0000000000000000000000000000000000000000000000000000000000000100 res=0000000000000000000000000000000000000000000000000000000000000000 NOTE: bits shift out right sideExpression: 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 sideExpression: -1 = -4 >> 2 Decimal: val=-4 res=-1 Binary: val=1111111111111111111111111111111111111111111111111111111111111100 res=1111111111111111111111111111111111111111111111111111111111111111 NOTE: bits shift out right sideExpression: -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 sideExpression: 4611686018427387904 = 4 << 60 Decimal: val=4 res=4611686018427387904 Binary: val=0000000000000000000000000000000000000000000000000000000000000100 res=0100000000000000000000000000000000000000000000000000000000000000Expression: -9223372036854775808 = 4 << 61 Decimal: val=4 res=-9223372036854775808 Binary: val=0000000000000000000000000000000000000000000000000000000000000100 res=1000000000000000000000000000000000000000000000000000000000000000 NOTE: sign bits get shifted outExpression: 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 sideExpression: -9223372036854775808 = -4 << 61 Decimal: val=-4 res=-9223372036854775808 Binary: val=1111111111111111111111111111111111111111111111111111111111111100 res=1000000000000000000000000000000000000000000000000000000000000000Expression: 0 = -4 << 62 Decimal: val=-4 res=0 Binary: val=1111111111111111111111111111111111111111111111111111111111111100 res=0000000000000000000000000000000000000000000000000000000000000000 NOTE: bits shift out left side, including sign bit
不要在 32 位系统下向右移超过 32 位。不要在结果可能超过 32 的情况下左移。使用 gmp 扩展对超出 PHP_INT_MAX 的数值来进行位操作。
参见 pack() , unpack() , gmp_and() , gmp_or() , gmp_xor() , gmp_testbit() 和 gmp_clrbit() 。
[#1] luis at rosety dot com [2015-10-18 14:05:49]
Example of function using bitwise operations for converting hexadecimal color (usually given as 6 hexadecimal digit string, into separated RGB integers)
<?php
function hex2rgb($hex){
$dec = hexdec($hexcolor); // $hex string to decimal value
$r = $dec & hexdec('FF0000'); //Mask for red
$g = $dec & hexdec('00FF00'); //Mask for green
$b = $dec & hexdec('0000FF'); //Mask for blue
return array($r>>16, $g>>8, $b); // Shift full right each color from its original position
}
?>
Example of use
<?php
$rgb = hex2rgb('112233');
echo "red: ".$rgb[0]."\n";
echo "green: ".$rgb[1]."\n";
echo "blue: ".$rgb[2]."\n";
?>
Would produce:
red: 17
green: 34
blue: 51
Since:
dechex(17) = '#11'
dechex(34) = '#22'
dechex(51) = '#33'
[#2] yesman at mail dot com [2014-09-23 03:33:47]
There is this useful bitwise calculator online for us to make quick calculations: http://www.bitwiseoperatorcalculator.com
[#3] J. Ketting [2014-07-12 21:39:04]
@zooly
Don't forget the leading zeros.
It's very important if you want to write a function similar to the assembly instructions 'ror' and 'rol' (Rotate on Right and Rotate on Left), because of dword value; rotating the binary always takes 32 positions and includes the leading zeros!
So this is the right way:
<?php
function rotate ($decimal, $bits) {
$binary = decbin($decimal);
$binary = str_pad($binary, 32, '0', STR_PAD_LEFT);
return (
bindec(substr($binary, $bits).substr($binary, 0, $bits))
);
}
?>
Look at this assembly code:
mov edx, 1bf5616c
ror edx, 8
After this operation: edx = 0x6c1bf561 (binary: 1101100000110111111010101100001)
But your code returns 0x0d9bf561 (binary: 1101100110111111010101100001)
In order to get the right value you have to add the leading zeros by adding that line with strpad() (see above). Very important!
[#4] melnikvictorl at gmail dot com [2013-09-20 15:08:40]
Nice example of bitwise operation with strings:
<?php
echo 'php' & '^_^';
// will output string PHP
[#5] sag at ich dot net [2013-07-14 18:52:45]
me reimplement for bitwise NOT (~)
protected function flipBin($number) {
$bin = str_pad(base_convert($number, 10, 2), 32, 0, STR_PAD_LEFT);
for ($i = 0; $i < 32; $i++) {
switch ($bin{$i}) {
case '0' :
$bin{$i} = '1';
break;
case '1' :
$bin{$i} = '0';
break;
}
}
return bindec($bin);
}
the benefit is, it works with numbers greater MAX_INT
[#6] vivekanand dot pathak25 at gmail dot com [2013-03-16 07:59:30]
$a = 9;
$b = 10;
echo $a & $b;
place value 128 64 32 16 8 4 2 1
$a 0 0 0 0 1 0 0 1 =9
$b 0 0 0 0 1 0 1 0 =10
result 8
only bit they share together is the 8 bit. So 8 gets returned.
$a = 36;
$b = 103;
echo $a & $b;
place value 128 64 32 16 8 4 2 1
$a 0 0 1 0 0 1 0 0 =36
$b 0 1 1 0 0 1 1 1 =103
result 32+4 = 36
the only bits these two share together are the bits 32 and 4 which when added together return 36.
$a = 9;
$b = 10;
echo $a | $b;
place value 128 64 32 16 8 4 2 1
$a 0 0 0 0 1 0 0 1 =9
$b 0 0 0 0 1 0 1 0 =10
result 8+2+1 = 11
3 bits set, in the 8, 2, and 1 column.add those up 8+2+1 and you get 11
$a = 9;
$b = 10;
echo $a ^ $b;
place value 128 64 32 16 8 4 2 1
$a 0 0 0 0 1 0 0 1 =9
$b 0 0 0 0 1 0 1 0 =10
result 2+1 = 3
the 2 bit and the 1 bit that they each have set but don't share. Soooo 2+1 = 3
[#7] erich at seachawaii dot com [2012-12-14 03:15:53]
Just a note regarding negative shift values, as the documentation states each shift is an integer multiply or divide (left or right respectively) by 2. That means a negative shift value (the right hand operand) effects the sign of the shift and NOT the direction of the shift as I would have expected.
FE. 0xff >> -2 results in 0x0
and 0xff << -2 result in 0xFFFFFFFFC0000000 (dependant on PHP_INT_MAX)
[#8] frankemeks77 at yahoo dot com [2012-08-21 13:24:50]
Just learning Bitwise Shift Operators.
The easiest way to resolve a bitwise shift operators is my multiply or dividing each step by two for left shift or right shift respectively
Example:
LEFT SHIFT
<?php echo 8 << 3; //64 ?>
//same as
<?php echo 8 * 2 * 2 * 2; ?>
RIGHT SHIFT
<?php echo 8 >> 3; //1 ?>
//same as
<?php echo ((8/2)/2)/2; //1 ?>
//Solving on a paper 8/2 = 4/2 = 2/2 = 1
[#9] wbcarts at juno dot com [2012-05-17 03:52:20]
BITWISE FLAGS for Custom PHP Objects
Sometimes I need a custom PHP Object that holds several boolean TRUE or FALSE values. I could easily include a variable for each of them, but as always, code has a way to get unweildy pretty fast. A more intelligent approach always seems to be the answer, even if it seems to be overkill at first.
I start with an abstract base class which will hold a single integer variable called $flags. This simple integer can hold 32 TRUE or FALSE boolean values. Another thing to consider is to just set certain BIT values without disturbing any of the other BITS -- so included in the class definition is the setFlag($flag, $value) function, which will set only the chosen bit. Here's the abstract base class definition:
<?php
# BitwiseFlag.php
abstract class BitwiseFlag
{
protected $flags;
protected function isFlagSet($flag)
{
return (($this->flags & $flag) == $flag);
}
protected function setFlag($flag, $value)
{
if($value)
{
$this->flags |= $flag;
}
else
{
$this->flags &= ~$flag;
}
}
}
?>
The class above is abstract and cannot be instantiated, so an extension is required. Below is a simple extension called User -- which is severely truncated for clarity. Notice I am defining const variables AND methods to use them.
<?php
# User.php
require('BitwiseFlag.php');
class User extends BitwiseFlag
{
const FLAG_REGISTERED = 1; // BIT #1 of $flags has the value 1
const FLAG_ACTIVE = 2; // BIT #2 of $flags has the value 2
const FLAG_MEMBER = 4; // BIT #3 of $flags has the value 4
const FLAG_ADMIN = 8; // BIT #4 of $flags has the value 8
public function isRegistered(){
return $this->isFlagSet(self::FLAG_REGISTERED);
}
public function isActive(){
return $this->isFlagSet(self::FLAG_ACTIVE);
}
public function isMember(){
return $this->isFlagSet(self::FLAG_MEMBER);
}
public function isAdmin(){
return $this->isFlagSet(self::FLAG_ADMIN);
}
public function setRegistered($value){
$this->setFlag(self::FLAG_REGISTERED, $value);
}
public function setActive($value){
$this->setFlag(self::FLAG_ACTIVE, $value);
}
public function setMember($value){
$this->setFlag(self::FLAG_MEMBER, $value);
}
public function setAdmin($value){
$this->setFlag(self::FLAG_ADMIN, $value);
}
public function __toString(){
return 'User [' .
($this->isRegistered() ? 'REGISTERED' : '') .
($this->isActive() ? ' ACTIVE' : '') .
($this->isMember() ? ' MEMBER' : '') .
($this->isAdmin() ? ' ADMIN' : '') .
']';
}
}
?>
This seems like a lot of work, but we have addressed many issues, for example, using and maintaining the code is easy, and the getting and setting of flag values make sense. With the User class, you can now see how easy and intuitive bitwise flag operations become.
<?php
require('User.php')
$user = new User();
$user->setRegistered(true);
$user->setActive(true);
$user->setMember(true);
$user->setAdmin(true);
echo $user; // outputs: User [REGISTERED ACTIVE MEMBER ADMIN]
?>
[#10] Anonymous [2011-10-12 07:56:00]
To make very clear why ("18" & "32") is "10".
1) they they are both strings ,
2) "&" operator works on strings by taking each !Character! from each string and make a bit wise & between them and add this value to the resulting string
So:
"18" is made up of two characters: 0x31, 0x38
"32" is made up of two characters: 0x33, 0x32
----RESULT-----
0x31 & 0x33 = 0x31 => "1"
0x38 & 0x32 = 0x30 => "0"
and the result is "10" which is 100% correct.
[#11] spencer-p-moy at example dot com [2011-09-17 18:21:14]
The NOT or complement operator ( ~ ) and negative binary numbers can be confusing.
~2 = -3 because you use the formula ~x = -x - 1 The bitwise complement of a decimal number is the negation of the number minus 1.
NOTE: just using 4 bits here for the examples below but in reality PHP uses 32 bits.
Converting a negative decimal number (ie: -3) into binary takes 3 steps:
1) convert the positive version of the decimal number into binary (ie: 3 = 0011)
2) flips the bits (ie: 0011 becomes 1100)
3) add 1 (ie: 1100 + 0001 = 1101)
You might be wondering how does 1101 = -3. Well PHP uses the method "2's complement" to render negative binary numbers. If the left most bit is a 1 then the binary number is negative and you flip the bits and add 1. If it is 0 then it is positive and you don't have to do anything. So 0010 would be a positive 2. If it is 1101, it is negative and you flip the bits to get 0010. Add 1 and you get 0011 which equals -3.
[#12] aba at example dot com [2011-08-12 05:07:51]
It is true that if both the left-hand and right-hand parameters are strings, the bitwise operator will operate on the characters' ASCII values. However, a complement is necessary to complete this sentence.
It is not irrelevant to point out that the decimal character's ASCII value have different binary values.
<?php
if (('18' & '32') == '10') {
echo ord('18'); //return decimal value 49, which have binary value 110001
echo ord('32'); //return decimal value 51, which have binary value 110011
echo ord('10'); //return decimal value 49, which have binary value 110001
//Therefore 110001 & 110011 = 110001
}
?>
[#13] ivoras at gmail dot com [2011-06-06 04:44:14]
As an additional curiosity, for some reason the result of the operation ("18" & "32") is "10". In other words, try avoiding using the binary operators on strings :)
[#14] josh at joshstrike dot com [2011-02-11 19:54:41]
More referencing this for myself than anything... if you need to iterate through every possible binary combination where $n number of flags are set to 1 in a mask of $bits length:
<?php
echo masksOf(3,10);
function masksOf($n,$bits) {
$u = pow(2,$bits)-1; //start value, full flags on.
$masks = array();
while ($u>0) {
$z = numflags($u);
if ($z==$n) array_push($masks,$u);
$u--;
}
return ($masks);
}
function numflags($n) {
$k = 0;
while ($n) {
$k += $n & 1;
$n = $n >> 1;
}
return ($k);
// alternately:
// $u = 0;
// for ($k=1;$k<=$n;$k*=2) {
// $u+=($n&$k?1:0);
// }
// return ($u);
}
?>
[#15] amckenzie4 at gmail dot com [2010-08-25 08:19:38]
If, like me, you've never thought about how PHP deals with binary, the output of the bitwise NOT may confuse you. For instance, this:
$bin = 2;
$notbin = ~$bin;
echo "Bin: " . decbin($bin) . " !bin: " . decbin($notbin) . "\n";
returns this:
Bin: 10 !bin: 1111111111111111111111111111111111111111111111111111111111111101
The reason is that all binary numbers are treated as 32 bits, even if you've manually entered less. In order to get the result I expected (01), it was necessary to AND the result with the number of bits I wanted: in this case, 2 (the number 3, in decimal). Be aware that all return values will have zeros removed from the left until they reach a bit that is set to 1. Continuing the above example, the following:
$notbin_2 = ~$bin & '3';
echo "!bin & 3: " . decbin($notbin_2) . "\n";
returns this:
!bin & 3: 1
Note that the actual value was a string of 31 zeros followed by a 1, but the zeros were not shown. This is probably a good thing.
Furthermore, the NOT operator uses two's complement, which means the number you get may be even stranger than you expect: using two's complement means that ~2 == -3. There are plenty of good explanations of two's complement online, so I won't go into that question here.
If what you want is just to reverse a string of bits without any interpretation, you can use a function like this:
function bitnot($bin)
{
$not = "";
for ($i = 0; $i < strlen($bin); $i++)
{
if($bin[$i] == 0) { $not .= '1'; }
if($bin[$i] == 1) { $not .= '0'; }
}
return $not;
}
It takes a binary string of any length, reverses the bits, and returns the new string. You can then treat it as a binary number, use bindec() to turn it into a decimal, or whatever you want.
I hope this helps someone as much as it would have helped me a week ago!
[#16] Core Xii [2010-08-11 10:24:29]
Be very careful when XOR-ing strings! If one of the values is empty (0, '', null) the result will also be empty!
<?php
var_dump(1234 ^ 0); // int(1234)
var_dump(1234 ^ ''); // int(1234)
var_dump(1234 ^ null); // int(1234)
var_dump('hello world' ^ 0); // int(0)
var_dump('hello world' ^ ''); // string(0) ""
var_dump('hello world' ^ null); // int(0)
?>
This seems rather inconsistent behavior. An integer XOR'd with zero results the original integer. But a string XOR'd with an empty value results an empty value!
My password hashing function was always returning the same hash... Because I was XOR-ing it with a salt that was sometimes empty!
[#17] Adam [2010-05-15 13:25:30]
Be careful of order of operations.
for example, you may want to check if the second bit is set:
<?php
if ($x & 2 == 2) {
}
?>
is different than
<?php
if (($x & 2) == 2) {
}
?>
and the latter of the two should be used.
[#18] Anonymous [2010-01-12 10:18:34]
Beware that PHP's << and >> operators, unlike the other bitwise operators do not work on ASCII values; << and >> cast their operands to integer (when possible) before shifting and will always return an integer result.
For example:
<?php
$foo = "1"; // chr(49)
var_dump($foo << 1); // Output is int(2)
$foo = "!"; // chr(33)
var_dump($foo << 1); // Output is int(0)
?>
[#19] Alex [2009-11-11 10:10:36]
Okay, I'm twelve, and I am new to PHP, but I created the following -simple- functions:
<?php
function Isdec($num) {
if(round($num)==$num) {
return false;
}
else {
return true;
}
}
function decshiftl($number, $amount){
if(Isdec($number)) {
$decimal = substr($number, (strlen($number) - round($number) + 1));
$decimal*=pow(10,strlen($decimal) - 1);
$Shiftl = ($number << $amount) + (($decimal << $amount) / pow(10,strlen($decimal)));
}
else {
$Shiftl = $number << $amount;
}
return $Shiftl;
}
function decshiftr($number, $amount){
if(Isdec($number)) {
$decimal = substr($number, (strlen($number) - round($number) + 1));
$decimal*=pow(10,strlen($decimal) - 1);
$Shiftr = ($number >> $amount) + (($decimal >> $amount) / pow(10,strlen($decimal)));
}
else {
$Shiftr = $number >> $amount;
}
return $Shiftr;
}
?>
Anyway, it's just shoving parameters in to an equation, but sometimes reinventing the wheel and putting rockets on it is a good thing.
[#20] zooly at globmi dot com [2009-10-19 07:52:48]
Here is an example for bitwise leftrotate and rightrotate.
Note that this function works only with decimal numbers - other types can be converted with pack().
<?php
function rotate ( $decimal, $bits) {
$binary = decbin($decimal);
return (
bindec(substr($binary, $bits).substr($binary, 0, $bits))
);
}
// Rotate 124 (1111100) to the left with 1 bits
echo rotate(124, 1);
// = 121 (1111001)
// Rotate 124 (1111100) to the right with 3 bits
echo rotate(124, -3);
// = 79 (1001111)
?>
[#21] grayda dot NOSPAM at DONTSPAM dot solidinc dot org [2009-06-03 20:48:55]
Initially, I found bitmasking to be a confusing concept and found no use for it. So I've whipped up this code snippet in case anyone else is confused:
<?php
// The various details a vehicle can have
$hasFourWheels = 1;
$hasTwoWheels = 2;
$hasDoors = 4;
$hasRedColour = 8;
$bike = $hasTwoWheels;
$golfBuggy = $hasFourWheels;
$ford = $hasFourWheels | $hasDoors;
$ferrari = $hasFourWheels | $hasDoors | $hasRedColour;
$isBike = $hasFourWheels & $bike; # False, because $bike doens't have four wheels
$isGolfBuggy = $hasFourWheels & $golfBuggy; # True, because $golfBuggy has four wheels
$isFord = $hasFourWheels & $ford; # True, because $ford $hasFourWheels
?>
And you can apply this to a lot of things, for example, security:
<?php
// Security permissions:
$writePost = 1;
$readPost = 2;
$deletePost = 4;
$addUser = 8;
$deleteUser = 16;
// User groups:
$administrator = $writePost | $readPosts | $deletePosts | $addUser | $deleteUser;
$moderator = $readPost | $deletePost | $deleteUser;
$writer = $writePost | $readPost;
$guest = $readPost;
// function to check for permission
function checkPermission($user, $permission) {
if($user & $permission) {
return true;
} else {
return false;
}
}
// Now we apply all of this!
if(checkPermission($administrator, $deleteUser)) {
deleteUser("Some User"); # This is executed because $administrator can $deleteUser
}
?>
Once you get your head around it, it's VERY useful! Just remember to raise each value by the power of two to avoid problems
[#22] Silver [2009-05-12 16:36:17]
Regarding what Bob said about flags, I'd like to point out there's a 100% safe way of defining flags, which is using hexadecimal notation for integers:
<?php
define("f0", 0x1); // 2^0
define("f1", 0x2); // 2^1
define("f2", 0x4); // 2^2
define("f3", 0x8); // 2^3
define("f4", 0x10); // 2^4
define("f5", 0x20); // 2^5
// ...
define("f20", 0x1000000); // 2^20
define("f21", 0x2000000); // 2^21
define("f22", 0x4000000); // 2^22
define("f23", 0x8000000); // 2^23
define("f24", 0x10000000); // 2^24
// ... up to 2^31
?>
I always avoid using decimal notation when I have a large amount of different flags, because it's very easy to misspell numbers like 2^20 (1048576).
[#23] Bob [2009-04-26 00:19:57]
Here is an easy way to use bitwise operation for 'flag' functionality.
By this I mean managing a set of options which can either be ON or OFF, where zero or more of these options may be set and each option may only be set once. (If you are familiar with MySQL, think 'set' datatype).
Note: to older programmers, this will be obvious.
Here is the code:
<?php
function set_bitflag()
{
$val = 0;
foreach(func_get_args() as $flag) $val = $val | $flag;
return $val;
}
function is_bitflag_set($val, $flag)
{
return (($val & $flag) === $flag);
}
// Define your flags
define('MYFLAGONE', 1); // 0001
define('MYFLAGTWO', 2); // 0010
define('MYFLAGTHREE', 4); // 0100
define('MYFLAGFOUR', 8); // 1000
?>
I should point out: your flags are stored in a single integer. You can store loads of flags in a single integer.
To use my functions, say you wanted to set MYFLAGONE and MYFLAGTHREE, you would use:
<?php
$myflags = set_bitflags(MYFLAGONE, MYFLAGTHREE);
?>
Note: you can pass set_bitflags() as many flags to set as you want.
When you want to test later if a certain flag is set, use e.g.:
<?php
if(is_bitflag_set($myflags, MYFLAGTWO))
{
echo "MYFLAGTWO is set!";
}
?>
The only tricky part is defining your flags. Here is the process:
1. Write a list of your flags
2. Count them
3. Define the last flag in your list as 1 times 2 to the power of <count> minus one. ( I.E. 1*2^(<count>-1) )
3. Working backwards through your list, from the last to the first, define each one as half of the previous one. You should reach 1 when you get to the first
If you want to understand binary numbers, bits and bitwise operation better, the wikipedia page explains it well - http://en.wikipedia.org/wiki/Bitwise_operation.
[#24] cw3theophilus at gmail dot com [2009-04-15 14:22:44]
For those who are looking for a circular bit shift function in PHP (especially useful for cryptographic functions) that works with negtive values, here is a little function I wrote:
(Note: It took me almost a whole day to get this to work with negative $num values (I couldn't figure out why it sometimes worked and other times didn't), because PHP only has an arithmatic and not a logical bitwise right shift like I am used to. I.e. 0x80000001>>16 will ouputs (in binary) "1111 1111 1111 1111 1000 0000 0000 0000" instead of "0000 0000 0000 0000 1000 0000 0000 0000" like you would expect. To fix this you have to apply the mask (by bitwise &) equal to 0x7FFFFFFF right shifted one less than the offset you are shifting by.)
<?php
function circular_shift($num,$offset) { //Do a nondestructive circular bitwise shift, if offset positive shift left, if negative shift right
$num=(int)$num;
$mask=0x7fffffff; //Mask to cater for the fact that PHP only does arithmatic right shifts and not logical i.e. PHP doesn't give expected output when right shifting negative values
if ($offset>0) {
$num=($num<<$offset%32) | (($num>>(32-$offset%32)) & ($mask>>(31-$offset%32)));
}
elseif ($offset<0){
$offset=abs($offset);
$num=(($num>>$offset%32) & ($mask>>(-1+$offset%32))) | ($num<<(32-$offset%32));
}
return $num;
}
?>
[#25] m0sh at hotmail dot com [2008-08-07 00:03:20]
@greenone - nice function, thanks. I've adapted it for key usage:
<?php
function bitxor($str, $key) {
$xorWidth = PHP_INT_SIZE*8;
// split
$o1 = str_split($str, $xorWidth);
$o2 = str_split(str_pad('', strlen($str), $key), $xorWidth);
$res = '';
$runs = count($o1);
for($i=0;$i<$runs;$i++)
$res .= decbin(bindec($o1[$i]) ^ bindec($o2[$i]));
return $res;
}
?>
[#26] bartbons at debster.nl [2008-04-07 02:27:55]
@kendsnyder at gmail dot com
Thanx for your great function. But your function is not 100% correct. It should be:
function safeBitCheck($number,$comparison) {
if( $number < 2147483647 ) {
return ($number & $comparison)==$comparison;
} else {
$binNumber = strrev(base_convert($number,10,2));
$binComparison = strrev(base_convert($comparison,10,2));
for( $i=0; $i<strlen($binComparison); $i++ ) {
if( strlen($binNumber) - 1 <$i || ($binComparison{$i}==="1" && $binNumber{$i}==="0") ) {
return false;
}
}
return true;
}
}
Mind the 'minus 1' on "if( strlen($binNumber) - 1 <$i".
cheers, Bart
[#27] forlamp at msn dot com [2007-12-06 18:35:50]
two's complement logical operation for 32-bit.
$x must be (int) when passing it to this function to work properly.
function comp2($x) // 32bit bitwise complement
{
$mask = 0x80000000;
if ($x < 0)
{
$x &= 0x7FFFFFFF;
$x = ~$x;
return $x ^ $mask;
}
else
{
$x = $x ^ 0x7FFFFFFF;
return $x | $mask;
}
}
[#28] gcG [2007-03-01 16:23:57]
function xnor($a, $b) {
return ~($a ^ $b);
}
XNOR is very usefull ;D
[#29] bryanagee at gmail dot com [2007-02-08 11:19:58]
I found the 31-bit limitation on the bitwise ands to be a bit frustrating in large scale permission control applications. I have a situation involving page-level access with more than 50 pages. I was able to workaround the limitation by adding a loop that dropped 31 bits off of the right until the resource identifier bit is within the first 31.
<?php
$userlevel = $session->userlevel - 0; # the subtraction ensures int type
$pg_code = pow(2,($pg_id-1));
while ($pg_code >= 2147483648) {
$pg_code = $pg_code/pow(2,31);
$userlevel = $session->userlevel/pow(2,31) ;
}
if (!($userlevel - 0 & $pg_code)) {
#if not authorized, show the unauthorized page
header('Location: Unauthorized.php');
exit;
}
?>
[#30] zewt at hotmail dot com [2007-01-11 23:28:46]
if you use bitwise you MUST make sure your variables are integers, otherwise you can get incorrect results.
I recommend ALWAYS
(int)$var & (int)$var2
This will save you many headaches when troubleshooting a completely illogical result.
[#31] rizal dot almashoor at gmail dot com [2006-11-13 16:12:50]
The following function will perform a 32-bit left shift on a 64-bit machine:
<?php
function leftshift32($number, $steps)
{
$binary = decbin($number).str_repeat("0", $steps);
$binary = str_pad($binary, 32, "0", STR_PAD_LEFT);
$binary = substr($binary, strlen($binary) - 32);
return $binary{0} == "1" ? -(pow(2, 31) - bindec(substr($binary, 1))) : bindec($binary);
}
?>
[#32] Xavier Daull [2006-02-04 13:34:37]
<?php
// Extract part of a binary data - due to windows system limitations (and this code), bit extracted length($firstbit to $lastbit included) is limited to 31 bits
function sub_bindata($mybindata, $firstbit = 7, $lastbit = 0, $highestbitfirst = true)
{
// 1. Create a bit mask of the right size by triming left and right
// 2. select bits by an AND on $mybindata
// 3. shift right to get only length needed
if($highestbitfirst) return (((0x7FFFFFFF >> (30+$lastbit-$firstbit))<<($lastbit)) & $mybindata) >> ($lastbit);
else return (((0x7FFFFFFF >> (30-$lastbit+$firstbit))<<(30-$lastbit)) & $mybindata) >> (30-$lastbit);
}
?>
[#33] John L [2006-02-02 14:11:39]
Bitwise operators are swell, but (a & b & c & d) == a
is not a way to test for four-way equality. If a is zero, it'll always be true, and in general it will be true any time a has no bits not also in the other three values.
[#34] Nina Cording [2005-10-29 09:50:03]
For those who were searching for a way to actually rotate the bits of a number, here are some little functions I wrote:
<?php
function bitRotate32($value,$amount) {
if ($amount>0) {
$amount %= 32;
$value = ($value<<$amount) | ($value>>(32-$amount));
} elseif ($amount<0) {
$amount = -$amount%32;
$value = ($value>>$amount) | ($value<<(32-$amount));
}
return $value;
}
function bitRotate($value,$amount,$bits) {
$mask = ($bits<32) ? 0x7fffffff >> (31-$bits) : 0xffffffff;
if ($amount>0) {
$amount %= $bits;
$value = ($value<<$amount) | ($value>>($bits-$amount));
} elseif ($amount<0) {
$amount = -$amount%$bits;
$value = ($value>>$amount) | ($value<<($bits-$amount));
}
return $value & $mask;
}
// test the rotation:
$test = 4123;
for ($i=0; $i<64; $i++) {
$value = bitRotate($test,-$i,8); // rotates 8 bits to the left (-$amount)
echo sprintf("%032b<br/>",$value);
}
?>
[#35] zlel grxnslxves13 at hotmail dot com~=s/x/ee/g [2005-10-26 19:30:45]
I refer to Eric Swanson's post on Perl VS PHP's implementation of xor.
Actually, this is not an issue with the implementation of XOR, but a lot more to do with the lose-typing policy that PHP adopts.
Freely switching between int and float is good for most cases, but problems happen when your value is near the word size of your machine. Which is to say, 32-bit machines will encounter problems with values that hover around 0x80000000 - primarily because PHP does not support unsigned integers.
using bindec/decbin would address this issue as a work-around to do unsigned-int xor, but here's the real picture (i'm not claiming that this code will perform better, but this would be a better pedagogical code):
<?php
function unsigned_xor32 ($a, $b)
{
$a1 = $a & 0x7FFF0000;
$a2 = $a & 0x0000FFFF;
$a3 = $a & 0x80000000;
$b1 = $b & 0x7FFF0000;
$b2 = $b & 0x0000FFFF;
$b3 = $b & 0x80000000;
$c = ($a3 != $b3) ? 0x80000000 : 0;
return (($a1 ^ $b1) |($a2 ^ $b2)) + $c;
}
$x = 3851235679;
$y = 43814;
echo "<br>This is the value we want";
echo "<br>3851262585";
echo "<br>The result of a native xor operation on integer values is treated as a signed integer";
echo "<br>".($x ^ $y);
echo "<br>We therefore perform the MSB separately";
echo "<br>".unsigned_xor32($x, $y);
?>
This is really foundation stuff, but for those of you who missed this in college, there seems to be something on 2's complement here:
http://www.evergreen.edu/biophysics/technotes/program/2s_comp.htm
[#36] zlel [2005-10-26 17:22:06]
Here're my 32-bit carry-discarding operations for those of you porting encryption algorithms from C.
Be warned that some of these are not very efficient compared to the native operations, especially when called by heavy-duty encryption algorithms - but not discarding the carry bit may not land you the same results you get in C, simply because PHP's bitwise operations were not designed to work on fixed-sized registers.
(If your ported encryption algo still doen't give you the same results, remember to check your Endian-ness!)
function _BF_SHR32 ($x, $bits)
{
if ($bits==0) return $x;
if ($bits==32) return 0;
$y = ($x & 0x7FFFFFFF) >> $bits;
if (0x80000000 & $x) {
$y |= (1<<(31-$bits));
}
return $y;
}
function _BF_SHL32 ($x, $bits)
{
if ($bits==0) return $x;
if ($bits==32) return 0;
$mask = (1<<(32-$bits)) - 1;
return (($x & $mask) << $bits) & 0xFFFFFFFF;
}
function _BF_GETBYTE ($x, $y)
{
return _BF_SHR32 ($x, 8 * $y) & 0xFF;
}
function _BF_OR32 ($x, $y)
{
return ($x | $y) & 0xFFFFFFFF;
}
function _BF_ADD32 ($x, $y)
{
$x = $x & 0xFFFFFFFF;
$y = $y & 0xFFFFFFFF;
$total = 0;
$carry = 0;
for ($i=0; $i<4; $i++) {
$byte_x = _BF_GETBYTE($x, $i);
$byte_y = _BF_GETBYTE($y, $i);
$sum = $byte_x + $byte_y;
$result = $sum & 0xFF;
$carryforward = _BF_SHR32($sum, 8);
$sum = $result + $carry;
$result = $sum & 0xFF;
$carry = $carryforward + _BF_SHR32($sum, 8);
$total = _BF_OR32(_BF_SHL32($result, $i*8), $total);
}
return $total;
}
[#37] Tbrendstrup [2005-09-29 05:23:56]
note that the shift operators are arithmetic, not logic like in C. You may get unexpected results with negative numbers, see http://en.wikipedia.org/wiki/Bitwise_operation
here's a function to do logic right shifts.
<?php
function lshiftright($var,$amt)
{
$mask = 0x40000000;
if($var < 0)
{
$var &= 0x7FFFFFFF;
$mask = $mask >> ($amt-1);
return ($var >> $amt) | $mask;
}
return $var >> $amt;
}
$val = -10;
printf("arithmetic shift on a negative integer<br>%1\$032b<br>%2\$032b<br>%1\$0d<br>%2\$0d<br>",$val, $val >> 1 );
printf("logic shift on a negative integer<br>%1\$032b<br>%2\$032b<br>%1\$0d<br>%2\$0d<br>",$val, lshiftright($val, 1));
printf("logic shift on a positive integer<br>%1\$032b<br>%2\$032b<br>%1\$0d<br>%2\$0d<br>",-$val, lshiftright(-$val, 1));
?>
gives the output:
arithmetic shift on a negative integer
11111111111111111111111111110110
11111111111111111111111111111011
-10
-5
logic shift on a negative integer
11111111111111111111111111110110
01111111111111111111111111111011
-10
2147483643
logic shift on a positive integer
00000000000000000000000000001010
00000000000000000000000000000101
10
5
[#38] Eric Swanson [2005-08-31 17:19:38]
Perl vs. PHP implementation of the ^ operator:
After attempting to translate a Perl module into PHP, I realized that Perl's implementation of the ^ operator is different than the PHP implementation. By default, Perl treats the variables as floats and PHP as integers. I was able to verify the PHP use of the operator by stating "use integer;" within the Perl module, which output the exact same result as PHP was using.
The logical decision would be to cast every variable as (float) when using the ^ operator in PHP. However, this will not yield the same results. After about a half hour of banging my head against the wall, I discovered a gem and wrote a function using the binary-decimal conversions in PHP.
function binxor($a, $b) {
return bindec(decbin((float)$a ^ (float)$b));
}
//normal PHP code will not yeild the same result as Perl
$result = 3851235679 ^ 43814; //= -443704711
//to get the same result as Perl
$result = binxor(3851235679, 43814); //= 3851262585
//YIPPEE!!!
//to see the differences, try the following
$a = 3851235679 XOR 43814;
$b = 3851235679 ^ 43814; //integer result
$c = (float)3851235679 ^ (float)43814; //same as $b
$d = binxor(3851235679, 43814); //same as Perl!!
echo("A: $a<br />");
echo("B: $b<br />");
echo("C: $c<br />");
echo("D: $d<br />");
[#39] S?b. [2005-04-19 07:28:19]
Another practical case...
<?php
header('Content-Type: text/plain') ;
// We want to know the power-2 based numbers of $x
$x = 9124 ;
$n = 1 ;
while ( $x > 0 ) {
if ( $x & 1 == 1 ) {
echo $n, "\n" ;
}
$n *= 2 ;
$x >>= 1 ;
}
// Will output...
// 4
// 32
// 128
// 256
// 512
// 8192
?>
[#40] S?b. [2005-03-04 15:13:40]
A bitwise operators practical case :
<?php
// We want to know the red, green and blue values of this color :
$color = 0xFEA946 ;
$red = $color >> 16 ;
$green = ($color & 0x00FF00) >> 8 ;
$blue = $color & 0x0000FF ;
printf('Red : %X (%d), Green : %X (%d), Blue : %X (%d)',
$red, $red, $green, $green, $blue, $blue) ;
// Will display...
// Red : FE (254), Green : A9 (169), Blue : 46 (70)
?>
[#41] icy at digitalitcc dot com [2005-02-23 23:24:29]
Say... you really want to have say... more than 31 bits available to you in your happy bitmask. And you don't want to use floats. So, one solution would to have an array of bitmasks, that are accessed through some kind of interface.
Here is my solution for this: A class to store an array of integers being the bitmasks. It can hold up to 66571993087 bits, and frees up unused bitmasks when there are no bits being stored in them.
<?php
DEFINE('INTEGER_LENGTH',31); // Stupid signed bit.
class bitmask
{
protected $bitmask = array();
public function set( $bit ) // Set some bit
{
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
$this->bitmask[$key] |= 1 << $bit;
}
public function remove( $bit ) // Remove some bit
{
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
$this->bitmask[$key] &= ~ (1 << $bit);
if(!$this->bitmask[$key])
unset($this->bitmask[$key]);
}
public function toggle( $bit ) // Toggle some bit
{
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
$this->bitmask[$key] ^= 1 << $bit;
if(!$this->bitmask[$key])
unset($this->bitmask[$key]);
}
public function read( $bit ) // Read some bit
{
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
return $this->bitmask[$key] & (1 << $bit);
}
public function stringin($string) // Read a string of bits that can be up to the maximum amount of bits long.
{
$this->bitmask = array();
$array = str_split( strrev($string), INTEGER_LENGTH );
foreach( $array as $key => $value )
{
if($value = bindec(strrev($value)))
$this->bitmask[$key] = $value;
}
}
public function stringout() // Print out a string of your nice little bits
{
$string = "";
$keys = array_keys($this->bitmask);
sort($keys, SORT_NUMERIC);
for($i = array_pop($keys);$i >= 0;$i--)
{
if($this->bitmask[$i])
$string .= sprintf("%0" . INTEGER_LENGTH . "b",$this->bitmask[$i]);
}
return $string;
}
public function clear() // Purge!
{
$this->bitmask = array();
}
public function debug() // See what's going on in your bitmask array
{
var_dump($this->bitmask);
}
}
?>
It treats a positive integer input as a bit, so you don't have to deal with the powers of 2 yourself.
<?php
$bitmask = new bitmask();
$bitmask->set(8979879); // Whatever
$bitmask->set(888);
if($bitmask->read(888))
print 'Happy!\n';
$bitmask->toggle(39393); // Yadda yadda
$bitmask->remove(888);
$bitmask->debug();
$bitmask->stringin("100101000101001000101010010101010
00000001000001");
print $bitmask->stringout() . "\n";
$bitmask->debug();
$bitmask->clear();
$bitmask->debug();
?>
Would produce:
Happy!
array(2) {
[289673]=>
int(65536)
[1270]=>
int(8388608)
}
0000000000000001001010001010010001010100101010100
0000001000001
array(2) {
[0]=>
int(355106881)
[1]=>
int(37970)
}
array(0) {
}
[#42] louis /at/ mulliemedia.com [2005-01-20 14:12:22]
Note that the ^ operator, unlike in some other languages, is *not* the same as the pow() function.
[#43] richard-slater.co.uk [2004-02-22 01:07:20]
For those (like me) who are trying to do bit masking with very large numbers, here is a useful function to do the work for you.
<?php
function isBitSet($bitMask, $bitMap)
{
return (bool) gmp_intval(gmp_div(gmp_and($bitMask, $bitMap),$bitMask));
}
?>
[#44] krang at krang dot org dot uk [2003-12-04 14:30:08]