位运算符

位运算符允许对整数中特定位的评估和操作。

位运算符
示例 名称 结果
$a & $b 与 (AND) $a$b 中都设置的位被设置。
$a | $b 或 (包含或) $a$b 中设置的位被设置。
$a ^ $b 异或 (排他或) $a$b 中设置但不在两者中都设置的位被设置。
~ $a 非 (NOT) $a 中设置的位未设置,反之亦然。
$a << $b 左移 $a 的位向左移动 $b 步(每一步表示“乘以二”)。
$a >> $b 右移 $a 的位向右移动 $b 步(每一步表示“除以二”)。

PHP 中的位移是算术的。从任一端移出的位将被丢弃。左移在右侧插入零,而符号位在左侧移出,这意味着操作数的符号不会保留。右移在左侧插入符号位的副本,这意味着操作数的符号将保留。

使用括号以确保所需的 优先级。例如,$a & $b == true 会先评估等效性,然后进行位与;而 ($a & $b) == true 会先进行位与,然后再进行等效性比较。

如果 &|^ 运算符的两个操作数都是字符串,则该运算将作用于组成字符串的字符的 ASCII 值,结果将是一个字符串。在所有其他情况下,两个操作数都将 转换为整数,结果将是一个整数。

如果 ~ 运算符的操作数是一个字符串,则该运算将作用于组成字符串的字符的 ASCII 值,结果将是一个字符串,否则操作数和结果将被视为整数。

<<>> 运算符的两个操作数和结果始终被视为整数。

     PHP's error_reporting ini setting uses bitwise values,
     providing a real-world demonstration of turning
     bits off. To show all errors, except for notices,
     the php.ini file instructions say to use:
     E_ALL & ~E_NOTICE
    

     This works by starting with E_ALL:
     00000000000000000111011111111111
     Then taking the value of E_NOTICE...
     00000000000000000000000000001000
     ... and inverting it via ~:
     11111111111111111111111111110111
     Finally, it uses AND (&) to find the bits turned
     on in both values:
     00000000000000000111011111110111
    

     Another way to accomplish that is using XOR (^)
     to find bits that are on in only one value or the other:
     E_ALL ^ E_NOTICE
    

     error_reporting can also be used to demonstrate turning bits on.
     The way to show just errors and recoverable errors is:
     E_ERROR | E_RECOVERABLE_ERROR
    

     This process combines E_ERROR
     00000000000000000000000000000001
     and
     00000000000000000001000000000000
     using the OR (|) operator
     to get the bits turned on in either value:
     00000000000000000001000000000001
    

示例 #1 对整数进行位与、位或和位异或运算

<?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 位与 \n";
foreach (
$values as $value) {
$result = $value & $test;
printf($format, $result, $value, '&', $test);
}

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

echo
"\n 位异或 \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 对字符串进行位异或运算

<?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
/*
* 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";
}
?>

上面的示例在 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 扩展中的函数进行位操作。

添加注释

用户贡献的注释 27 个注释

wbcarts at juno dot com
12 年前
自定义 PHP 对象的位标志

有时我需要一个自定义的 PHP 对象,它包含多个布尔值 TRUE 或 FALSE。我可以轻松地为每个值添加一个变量,但正如预期的那样,代码很快就会变得笨拙。更智能的方法似乎总能解决问题,即使它最初看起来过于复杂。

我从一个抽象基类开始,该类将包含一个名为 $flags 的单个整数变量。这个简单的整数可以保存 32 个布尔值 TRUE 或 FALSE。另一个要考虑的是只设置某些位的值而不影响任何其他位 - 因此类定义中还包括 setFlag($flag, $value) 函数,该函数将只设置选定的位。以下是抽象基类的定义

<?php

# BitwiseFlag.php

abstract class BitwiseFlag
{
protected
$flags;

/*
* 注意:这些函数被设为 protected,以防止外部代码
* 错误地设置 BITS。请参考扩展类 'User' 如何处理。
*
*/
protected function isFlagSet($flag)
{
return ((
$this->flags & $flag) == $flag);
}

protected function
setFlag($flag, $value)
{
if(
$value)
{
$this->flags |= $flag;
}
else
{
$this->flags &= ~$flag;
}
}
}

?>

上面的类是抽象类,不能直接实例化,需要进行扩展。下面是一个简单的扩展类叫做 User,为了清晰起见,代码被截断。注意我定义了常量变量和使用它们的方法。

<?php

# User.php

require('BitwiseFlag.php');

class
User extends BitwiseFlag
{
const
FLAG_REGISTERED = 1; // $flags 的 BIT #1 的值为 1
const FLAG_ACTIVE = 2; // $flags 的 BIT #2 的值为 2
const FLAG_MEMBER = 4; // $flags 的 BIT #3 的值为 4
const FLAG_ADMIN = 8; // $flags 的 BIT #4 的值为 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' : '') .
']';
}
}

?>

这看起来像是很多工作,但我们已经解决了许多问题,例如,使用和维护代码很容易,并且获取和设置标志值非常直观。使用 User 类,您现在可以了解位标志操作变得多么容易和直观。

<?php

require('User.php')

$user = new User();
$user->setRegistered(true);
$user->setActive(true);
$user->setMember(true);
$user->setAdmin(true);

echo
$user; // 输出:User [REGISTERED ACTIVE MEMBER ADMIN]

?>
grayda dot NOSPAM at DONTSPAM dot solidinc dot org
15 年前
最初,我发现位掩码是一个令人困惑的概念,而且没有找到它的用武之地。所以我写了这段代码片段,以防其他人也感到困惑。

<?php

// 车辆可能具有的各种细节
$hasFourWheels = 1;
$hasTwoWheels = 2;
$hasDoors = 4;
$hasRedColour = 8;

$bike = $hasTwoWheels;
$golfBuggy = $hasFourWheels;
$ford = $hasFourWheels | $hasDoors;
$ferrari = $hasFourWheels | $hasDoors | $hasRedColour;

$isBike = $hasFourWheels & $bike; # False,因为 $bike 没有四个轮子
$isGolfBuggy = $hasFourWheels & $golfBuggy; # True,因为 $golfBuggy 有四个轮子
$isFord = $hasFourWheels & $ford; # True,因为 $ford $hasFourWheels

?>

您可以将其应用于许多事物,例如安全。

<?php

// 安全权限:
$writePost = 1;
$readPost = 2;
$deletePost = 4;
$addUser = 8;
$deleteUser = 16;

// 用户组:
$administrator = $writePost | $readPosts | $deletePosts | $addUser | $deleteUser;
$moderator = $readPost | $deletePost | $deleteUser;
$writer = $writePost | $readPost;
$guest = $readPost;

// 检查权限的函数
function checkPermission($user, $permission) {
if(
$user & $permission) {
return
true;
} else {
return
false;
}
}

// 现在我们应用这一切!
if(checkPermission($administrator, $deleteUser)) {
deleteUser("Some User"); # 这条语句会被执行,因为 $administrator 有 $deleteUser 权限
}

?>

一旦您理解了它,它就非常有用!只需记住将每个值乘以二的幂,以避免问题。
S?b.
19 年前
位运算符的实际案例

<?php
// 我们想要知道这个颜色的红色、绿色和蓝色值:
$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) ;

// 将显示...
// Red : FE (254), Green : A9 (169), Blue : 46 (70)
?>
frankemeks77 at yahoo dot com
11 年前
刚学习位移运算符。

解决位移运算符的最简单方法是将每个步骤分别乘以或除以 2,分别对应左移或右移。

示例

左移
<?php echo 8 << 3; //64 ?>

// 等同于
<?php echo 8 * 2 * 2 * 2; ?>

右移
<?php echo 8 >> 3; //1 ?>

// 等同于
<?php echo ((8/2)/2)/2; //1 ?>

// 在纸上进行运算:8/2 = 4/2 = 2/2 = 1
Silver
15 年前
关于 Bob 关于标志的说法,我想指出,有一个 100% 安全的方法来定义标志,那就是使用整数的十六进制表示法。

<?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
// ... 直到 2^31
?>

当我有大量的不同标志时,我总是避免使用十进制表示法,因为很容易拼错像 2^20 (1048576) 这样的数字。
m0sh at hotmail dot com
16 年前
@greenone - 漂亮的函数,谢谢。我为密钥使用对其进行了调整

<?php
function bitxor($str, $key) {
$xorWidth = PHP_INT_SIZE*8;
// 分割
$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;
}
?>
zewt at hotmail dot com
17 年前
如果你使用位运算,你必须确保你的变量是整数,否则你会得到错误的结果。

我建议总是

(int)$var & (int)$var2

这将帮助你在排查完全不合逻辑的结果时避免很多头痛。
zlel grxnslxves13 at hotmail dot com~=s/x/ee/g
18 年前
我参考了 Eric Swanson 关于 Perl VS PHP 的异或实现的帖子。

实际上,这不是异或实现的问题,而更多的是 PHP 采用的松散类型策略。

在 int 和 float 之间自由切换对于大多数情况来说很好,但是当你的值接近机器的字长时会发生问题。也就是说,32 位机器会遇到值在 0x80000000 附近时的问题 - 主要是因为 PHP 不支持无符号整数。

使用 bindec/decbin 作为解决无符号 int 异或的变通方法,但这里才是真正的情况(我并不声称此代码会执行得更好,但这将是一个更好的教学代码)

<?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>这是我们想要的值";
echo
"<br>3851262585";

echo
"<br>对整数类型进行原生异或运算的结果将被视为带符号整数";
echo
"<br>".($x ^ $y);

echo
"<br>因此,我们分别执行 MSB";
echo
"<br>".unsigned_xor32($x, $y);

?>

这确实是基础知识,但对于那些在大学里错过这些知识的人来说,这里似乎有一些关于二进制补码的内容。

http://www.evergreen.edu/biophysics/technotes/program/2s_comp.htm
zooly at globmi dot com
14 年前
以下是一个关于位左移和右移的示例。

请注意,此函数仅适用于十进制数字 - 其他类型可以使用 pack() 进行转换。

<?php

function rotate ( $decimal, $bits) {

$binary = decbin($decimal);

return (
bindec(substr($binary, $bits).substr($binary, 0, $bits))
);

}

// 将 124 (1111100) 向左旋转 1 位

echo rotate(124, 1);

// = 121 (1111001)

// 将 124 (1111100) 向右旋转 3 位

echo rotate(124, -3);

// = 79 (1001111)

?>
vivekanand dot pathak25 at gmail dot com
11 年前
$a = 9;
$b = 10;
echo $a & $b;

位值 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

结果 8

它们共有的唯一位是第 8 位。所以返回 8。

$a = 36;
$b = 103;
echo $a & $b;

位值 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

结果 32+4 = 36
这两个数共有的唯一位是第 32 位和第 4 位,加起来返回 36。

$a = 9;
$b = 10;
echo $a | $b;

位值 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

结果 8+2+1 = 11
3 位被设置,在第 8 列、第 2 列和第 1 列。将它们加起来 8+2+1,你得到 11。

$a = 9;
$b = 10;
echo $a ^ $b;

位值 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

结果 2+1 = 3
第 2 位和第 1 位,它们各自被设置,但没有共享。所以 2+1 = 3
josh at joshstrike dot com
13 年前
更多的是我自己参考这个... 如果你需要迭代所有可能的二进制组合,其中 $n 个标志在 $bits 长度的掩码中被设置为 1

<?php
echo masksOf(3,10);

function
masksOf($n,$bits) {
$u = pow(2,$bits)-1; //起始值,所有标志都打开。
$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);

// 或者:
// $u = 0;
// for ($k=1;$k<=$n;$k*=2) {
// $u+=($n&$k?1:0);
// }
// return ($u);
}
?>
spencer-p-moy at example dot com
12 年前
NOT 运算符或补码运算符( ~ )以及负二进制数可能令人困惑。

~2 = -3,因为你使用公式 ~x = -x - 1 十进制数的按位补码是该数的负数减 1。

注意:以下示例仅使用 4 位,但在现实中 PHP 使用 32 位。

将负十进制数(例如:-3)转换为二进制数需要 3 个步骤
1) 将十进制数的正数版本转换为二进制数(例如:3 = 0011)
2) 翻转位(例如:0011 变成 1100)
3) 加 1(例如:1100 + 0001 = 1101)

你可能想知道 1101 如何等于 -3。PHP 使用“二进制补码”方法来呈现负二进制数。如果最左边的位是 1,那么该二进制数为负数,你需要翻转位并加 1。如果是 0,那么它是正数,你无需进行任何操作。所以 0010 将是正 2。如果是 1101,那么它是负数,你需要翻转位得到 0010。加 1 你得到 0011,等于 -3。
ASchmidt at Anamera dot net
5 年前
在位掩码中设置、取消设置和测试单个位和多个位

<?php
const FLAG_A = 0b0001,
FLAG_B = 0b0010,
FLAG_C = 0b0100,
FLAG_D = 0b1000;

const
COMBO_BC = FLAG_B | FLAG_C;

$bitmask = 0b000;

// 设置单个标志。
$bitmask |= FLAG_B; // 设置 FLAG_B (=2)
$bitmask |= FLAG_C; // 也设置 FLAG_C (=4)

// 测试单个标志或多个标志。
echo (bool)( $bitmask & FLAG_B ); // True,B 被设置。

echo (bool)( $bitmask & (FLAG_A | FLAG_B) ); // True,A 或 B 被设置。

echo (bool)( $bitmask & FLAG_B and $bitmask & FLAG_C ); // True,B 和 C 被设置。
echo (bool)( ( $bitmask & (FLAG_B | FLAG_C) ) ^ (FLAG_B | FLAG_C) ); // False,如果 B 和 C 被设置。
echo (bool)( ( $bitmask & COMBO_BC ) ^ COMBO_BC ); // False,如果 B 和 C 被设置。

echo (bool)( $bitmask & FLAG_C and $bitmask & FLAG_D ); // False,C 和 D 没有 BOTH 被设置。
echo (bool)( ( $bitmask & (FLAG_C | FLAG_D) ) ^ (FLAG_C | FLAG_D) ); // True,如果 C 和 D 没有 BOTH 被设置。

// 重置单个标志。
$bitmask &= $bitmask ^ FLAG_B; // 取消设置 B
$bitmask &= $bitmask ^ FLAG_A; // A 仍然没有设置。
var_dump( $bitmask ); // 只有 C 仍然被设置 (=4)

// 重置多个标志。
$bitmask &= $bitmask ^ ( FLAG_C | FLAG_D ); // 取消设置 C 和/或 D
var_dump( $bitmask ); // 没有标志被设置 (=0)
aba at example dot com
13 年前
如果左右参数都是字符串,那么按位运算符确实会对字符的 ASCII 值进行运算。但是,需要补充说明才能使这句话完整。
指出十进制字符的 ASCII 值具有不同的二进制值并不是无关紧要的。

<?php
if (('18' & '32') == '10') {
echo
ord('18'); //返回十进制值 49,其二进制值为 110001
echo ord('32'); //返回十进制值 51,其二进制值为 110011
echo ord('10'); //返回十进制值 49,其二进制值为 110001
//因此 110001 & 110011 = 110001
}
?>
amckenzie4 at gmail dot com
13 年前
如果你像我一样,从未想过 PHP 如何处理二进制数,那么按位 NOT 的输出可能会让你困惑。例如,这样

$bin = 2;
$notbin = ~$bin;

echo "Bin: " . decbin($bin) . " !bin: " . decbin($notbin) . "\n";

返回这样

Bin: 10 !bin: 1111111111111111111111111111111111111111111111111111111111111101

原因是所有二进制数都被视为 32 位,即使你手动输入了更少的位。为了获得我期望的结果(01),需要将结果与我想要的位数进行 AND 运算:在本例中是 2(十进制数 3)。请注意,所有返回值都会从左侧移除零,直到它们到达一个设置为 1 的位。继续上面的示例,以下

$notbin_2 = ~$bin & '3';
echo "!bin & 3: " . decbin($notbin_2) . "\n";

返回这样

!bin & 3: 1

请注意,实际的值是 31 个零后跟一个 1 的字符串,但零没有显示。这可能是一件好事。

此外,NOT 运算符使用二进制补码,这意味着你得到的结果可能比你预期的更奇怪:使用二进制补码意味着 ~2 == -3。网上有很多关于二进制补码的优秀解释,所以这里不再讨论这个问题。

如果你只是想反转一串位,而不进行任何解释,可以使用这样的函数

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;
}

它接受任意长度的二进制字符串,反转位,并返回新的字符串。然后你可以将其视为二进制数,使用 bindec() 将其转换为十进制数,或者你想要的任何操作。

希望这对像我一样一周前需要帮助的人有所帮助!
Tbrendstrup
18 年前
请注意,移位运算符是算术运算符,而不是像 C 语言中的逻辑运算符。对于负数,你可能会得到意想不到的结果,请查看 http://en.wikipedia.org/wiki/Bitwise_operation

以下是一个执行逻辑右移的函数。

<?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("算术移位负整数<br>%1\$032b<br>%2\$032b<br>%1\$0d<br>%2\$0d<br>",$val, $val >> 1 );

printf("逻辑移位负整数<br>%1\$032b<br>%2\$032b<br>%1\$0d<br>%2\$0d<br>",$val, lshiftright($val, 1));

printf("逻辑移位正整数<br>%1\$032b<br>%2\$032b<br>%1\$0d<br>%2\$0d<br>",-$val, lshiftright(-$val, 1));
?>

输出结果

算术移位负整数
11111111111111111111111111110110
11111111111111111111111111111011
-10
-5

逻辑移位负整数
11111111111111111111111111110110
01111111111111111111111111111011
-10
2147483643

逻辑移位正整数
00000000000000000000000000001010
00000000000000000000000000000101
10
5
Eric Swanson
18 年前
Perl 和 PHP 中 ^ 运算符的实现

在尝试将 Perl 模块转换为 PHP 后,我意识到 Perl 中 ^ 运算符的实现与 PHP 中的实现不同。默认情况下,Perl 将变量视为浮点数,而 PHP 将变量视为整数。我通过在 Perl 模块中声明 "use integer;" 来验证 PHP 对运算符的使用,这产生了与 PHP 使用的完全相同的结果。

逻辑上,在 PHP 中使用 ^ 运算符时,应该将每个变量都强制转换为 (float)。但是,这不会产生相同的结果。经过大约半小时的苦苦挣扎,我发现了一个宝石,并使用 PHP 中的二进制-十进制转换编写了一个函数。

/*
由于我对位运算不太熟悉,我无法确定这是否是最佳解决方案,但它肯定是一个最终有效的解决方案,并且始终返回 Perl 提供的完全相同的结果。
*/
function binxor($a, $b) {
return bindec(decbin((float)$a ^ (float)$b));
}

// 普通的 PHP 代码不会产生与 Perl 相同的结果
$result = 3851235679 ^ 43814; //= -443704711

// 要获得与 Perl 相同的结果
$result = binxor(3851235679, 43814); //= 3851262585
// 太棒了!

// 要查看差异,请尝试以下操作
$a = 3851235679 XOR 43814;
$b = 3851235679 ^ 43814; // 整数结果
$c = (float)3851235679 ^ (float)43814; // 与 $b 相同
$d = binxor(3851235679, 43814); // 与 Perl 相同!

echo("A: $a<br />");
echo("B: $b<br />");
echo("C: $c<br />");
echo("D: $d<br />");
icy at digitalitcc dot com
19 年前
假设你想要在你的位掩码中使用超过 31 位。并且你不想使用浮点数。所以,一个解决方案是使用一个位掩码数组,通过某种接口访问它们。

以下是我针对这种情况的解决方案:一个类,用于存储一个整数数组,作为位掩码。它可以保存高达 66571993087 位,并在没有存储位的位掩码时释放它们。

<?php
/*
无限* 位和一般位处理。

* 不是无限的,抱歉。

可见,位掩码类在存储位时的唯一限制将是
索引号的最大限制,在 32 位整数系统上为 2^31 - 1,
所以 2^31 * 31 - 1 = 66571993087 位,假设浮点数为 64 位或其他。
我相信这对于任何事情来说都足够了。希望如此 :D.
*/

DEFINE('INTEGER_LENGTH',31); // 愚蠢的符号位。

class bitmask
{
protected
$bitmask = array();

public function
set( $bit ) // 设置某个位
{
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
$this->bitmask[$key] |= 1 << $bit;
}

public function
remove( $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 ) // 切换某个位
{
$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 ) // 读取某个位
{
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
return
$this->bitmask[$key] & (1 << $bit);
}

public function
stringin($string) // 读取一个位字符串,最多可以是最大位数。
{
$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() // 打印出你的漂亮的位字符串
{
$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() // 清除!
{
$this->bitmask = array();
}

public function
debug() // 查看你的位掩码数组中发生了什么
{
var_dump($this->bitmask);
}
}
?>

它将正整数输入视为一位,因此你无需自己处理 2 的幂。

<?php
$bitmask
= new bitmask();

$bitmask->set(8979879); // 随便什么

$bitmask->set(888);

if(
$bitmask->read(888))
print
'开心!\n';

$bitmask->toggle(39393); // 等等等等

$bitmask->remove(888);

$bitmask->debug();

$bitmask->stringin("100101000101001000101010010101010
00000001000001"
);

print
$bitmask->stringout() . "\n";

$bitmask->debug();

$bitmask->clear();

$bitmask->debug();
?>

会产生

开心!

array(2) {
[289673]=>
int(65536)
[1270]=>
int(8388608)
}

0000000000000001001010001010010001010100101010100
0000001000001

array(2) {
[0]=>
int(355106881)
[1]=>
int(37970)
}

array(0) {
}
ivoras at gmail dot com
13 年前
另外一个奇怪的地方是,运算结果("18" & "32")是 "10"。换句话说,尽量避免对字符串使用二进制运算符 :)
匿名
12 年前
为了清楚说明为什么 ("18" & "32") 等于 "10"。
1) 它们都是字符串,
2) "&" 运算符在字符串上工作,它将从每个字符串中获取每个 !字符!,并对它们进行按位 & 运算,并将此值添加到结果字符串中

所以
"18" 由两个字符组成:0x31、0x38
"32" 由两个字符组成:0x33、0x32
----结果-----
0x31 & 0x33 = 0x31 => "1"
0x38 & 0x32 = 0x30 => "0"

结果是 "10",这完全正确。
forlamp at msn dot com
16 年前
32 位的二进制补码逻辑运算。

$x 在传递给此函数时必须为 (int) 才能正常工作。

function comp2($x) // 32 位按位补码
{
$mask = 0x80000000;

if ($x < 0)
{
$x &= 0x7FFFFFFF;
$x = ~$x;

return $x ^ $mask;
}
else
{
$x = $x ^ 0x7FFFFFFF;

return $x | $mask;
}
}
cw3theophilus at gmail dot com
15 年前
对于那些正在寻找 PHP 中循环位移函数(特别是在加密函数中很有用)并且可以处理负值的人,这里是我写的一个小函数

(注意:我花了一整天的时间才让它处理负 $num 值(我无法弄清楚为什么有时它可以工作,而有时却不行),因为 PHP 只有算术位移,而没有像我习惯的逻辑位移。例如,0x80000001>>16 将输出(以二进制形式)"1111 1111 1111 1111 1000 0000 0000 0000",而不是 "0000 0000 0000 0000 1000 0000 0000 0000",就像你期望的那样。为了解决这个问题,你必须应用一个掩码(通过按位 &),该掩码等于 0x7FFFFFFF 向右移位比你移位的偏移量少一个。)

<?php
function circular_shift($num,$offset) { // 执行非破坏性循环按位移位,如果偏移量为正,则向左移位,如果偏移量为负,则向右移位
$num=(int)$num;
$mask=0x7fffffff; // 掩码用于解决 PHP 只有算术右移而没有逻辑右移的问题,例如,PHP 在右移负值时不会给出预期的输出
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;
}
?>
核心 Xii
14 年前
在对字符串进行异或运算时要非常小心!如果其中一个值为空(0、''、null),结果也将为空!

<?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)
?>

这似乎是相当不一致的行为。一个整数与零异或得到原始整数。但一个字符串与一个空值异或得到一个空值!

我的密码哈希函数总是返回相同的哈希值... 因为我用一个有时为空的盐与它异或!
sag at ich dot net
11 年前
我重新实现按位非 (~)

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);
}

好处是,它可以处理大于 MAX_INT 的数字
亚当
14 年前
注意运算顺序。

例如,你可能想检查第二位是否设置

<?php
if ($x & 2 == 2) {
/* 代码 */
}
?>

不同于

<?php
if (($x & 2) == 2) {
/* 代码 */
}
?>

应该使用后一种方式。
erich at seachawaii dot com
11 年前
关于负移位值的一个说明,正如文档中所述,每次移位都是一个整数乘以或除以(分别向左或向右)2。这意味着负移位值(右操作数)会影响移位的符号,而不是移位的方向,正如我所期望的那样。
例如,0xff >> -2 的结果为 0x0
而 0xff << -2 的结果为 0xFFFFFFFFC0000000(取决于 PHP_INT_MAX)
鲍勃
15 年前
这里有一个简单的方法,可以使用按位运算来实现 '标志' 功能。
我的意思是管理一组选项,这些选项可以是 ON 或 OFF,其中这些选项中的零个或多个可以被设置,并且每个选项只能设置一次。(如果你熟悉 MySQL,可以参考 'set' 数据类型)。
注意:对于老程序员来说,这将是显而易见的。

代码如下
<?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('MYFLAGONE', 1); // 0001
define('MYFLAGTWO', 2); // 0010
define('MYFLAGTHREE', 4); // 0100
define('MYFLAGFOUR', 8); // 1000
?>

我应该指出:你的标志存储在一个整数中。你可以在一个整数中存储大量的标志。

要使用我的函数,假设你想要设置 MYFLAGONE 和 MYFLAGTHREE,你可以使用
<?php
$myflags
= set_bitflags(MYFLAGONE, MYFLAGTHREE);
?>
注意:你可以在 set_bitflags() 中传递任意数量的标志来设置。

当你想要稍后测试某个标志是否设置时,可以使用例如
<?php
if(is_bitflag_set($myflags, MYFLAGTWO))
{
echo
"MYFLAGTWO 被设置了!";
}
?>

唯一棘手的地方是定义你的标志。以下是步骤:
1. 列出你的标志
2. 统计标志数量
3. 将列表中的最后一个标志定义为 1 乘以 2 的 <数量> 减 1 次方。(例如:1*2^(<数量>-1))
4. 从最后一个标志开始,逆序遍历列表,将每个标志定义为前一个标志的一半。当到达第一个标志时,应该得到 1

如果你想更好地理解二进制数、位和位运算,维基百科页面提供了详细的解释 - http://en.wikipedia.org/wiki/Bitwise_operation.
To Top