Web студия "GrandView"
  Главная   Написать Контакты
   
   
О проекте
Руководство php
 

Побитовые операторы

Побитовые операторы позволяют устанавливать конкретные биты в 0 или 1 для целочисленных значений. В случае если и левый, и правый операнды строки, побитовые операции будут работать с их ASCII-представлениями.

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

Таблица 15-3. Побитовые операторы

ПримерНазваниеРезультат
$a & $bПобитовое 'и'Устанавливаются только те биты, которые установлены и в $a, и в $b.
$a | $bПобитовое 'или'Устанавливаются те биты, которые установлены либо в $a, либо в $b.
$a ^ $bИсключающее или Устанавливаются только те биты, которые установлены либо только в $a, либо только в $b
~ $aОтрицание Устанавливаются те биты, которые в $a не установлены, и наоборот.
$a << $bСдвиг влево Все биты переменной $a сдвигаються на $b позиций влево (каждая позиция подразумевает 'умножение на 2')
$a >> $bСдвиг вправо Все биты переменной $a сдвигаються на $b позиций вправо (каждая позиция подразумевает 'деление на 2')

Внимание

Не используйте сдвиг вправо более чем на 32 бита на тридцатидвухразрядных системах. Не используйте сдвиг вправо для получения чисел, требующих для записи более ридцатидвух бит.



add a note add a note User Contributed Notes
Побитовые операторы
Visual
20-Jun-2007 06:42
To perform bitwise operations on floats, or anything for that matter, in the binary representation of your choice, simply use pack() on the data with the appropriate parameters, then you can operate on the resulting strings. in the end just unpack()
kendsnyder at gmail dot com
01-Jun-2007 03:30
I needed to perform a bitwise AND on floats and came up with this.  There is probably a better way to do this with bit rotation, but this works.

/**
* Perform a float-safe bitwise AND comparison
*
* @param int/float $number  The number to which to perform a bitwise AND
* @param int/float $comparison  The number with which to perform a bitwise AND
* @return bool
*/
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)<$i || ($binComparison{$i}==="1" && $binNumber{$i}==="0") ) {
                return false;  
            }
        }
        return true;
    }
}

safeBitCheck(17,16); // true
safeBitCheck(17,2); // false
safeBitCheck((4294967296+8589934592),4294967296); // true
safeBitCheck(2,8589934592); // false
don dot hosek at gmail dot com
24-Apr-2007 06:05
Don't forget operator precedence. == has a higher precedence than does & so in the cases that you've written

if (9 & 8 == 8) echo 'true'; else echo 'false';

is the same as 9 & (8 ==8) which is 9 & true or 9 & 1 which is 1 which is true

if (8 & 8 == 8) echo 'true'; else echo 'false';

is the same as 8 & (8 == 8) which is 8 & true or 8 & 1 which is 0 which is false

Putting in parentheses is required to cause the bitwise and to take place before the comparison.
sgarner at expio dot co dot nz
22-Apr-2007 10:04
The bitwise AND operator behaves oddly inside an IF statement when used with equal arguments.

<?php
echo 9 & 8;
// outputs '8' (correct)

echo 8 & 8;
// outputs '8' (correct)

if (9 & 8 == 8) echo 'true'; else echo 'false';
// outputs 'true' (correct)

if (8 & 8 == 8) echo 'true'; else echo 'false';
// outputs 'false' (wrong!!)
?>

The solution is to add parentheses:

<?php
if ((8 & 8) == 8) echo 'true'; else echo 'false';
// outputs 'true' (correct)
?>

Tested on PHP 5.2.1
gcG
01-Mar-2007 04:23
function xnor($a, $b) {
    return ~($a ^ $b);
}
XNOR is very usefull ;D
bryanagee at gmail dot com
08-Feb-2007 11:19
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;
        }
?>
zewt at hotmail dot com
11-Jan-2007 11:28
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.
rizal dot almashoor at gmail dot com
13-Nov-2006 04:12
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);
}
?>
keuleu at hotmail dot com
20-Sep-2006 03:10
<?php
function xor_swap(&$a, &$b) {
   if(
$a==$b) {
       return;
   }
  
$c = $a ^ $b;
  
$b = $c ^ $b;
  
$a = $c ^ $a;
}
?>

Explanation:

$c=$a*^$b*;
$b=$c^$b*; => $b=($a*^$b*)^$b*; => $b=$a*^0; => $b=$a*;
$a=$c^$a*; => $a=($a*^$b*)^$a; => $a=$b*^0; =>$a=$b*;

*original value of the variable

Now a version of xor_swap() with no buffer ($c):

<?php
function xor_swap(&$a,&$b){
   if(
$a==$b) {
       return;
   }
  
$a=$a^$b;
  
$b=$a^$b;
  
$a=$a^$b;
}
?>

Explanation:
$a=$a*^$b*;
$b=$a^$b*; => $b=($a*^$b*)^$b*; => $b=$a*;
$a=$a^$b; => $a=($a*^$b*)^$a*; =>$a=$b*;

* original value of the variable
~
28-Aug-2006 03:49
<?php
function xor_swap(&$a, &$b) {
    if(
$a==$b) {
        return;
    }
   
$c = $a ^ $b
   
$b = $c ^ $b;
   
$a = $c ^ $a;
}
function
swap (&$a, &$b) {
   
$buffer = $a;
   
$a = $b;
   
$b = $buffer;
}
$a = 1; $b = 2;
echo
'$a is '.$a.'<br />$b is '.$b.'<hr />';
swap($a, $b);
echo
'$a is '.$a.'<br />$b is '.$b.'<hr />';
xor_swap($a, $b);
echo
'$a is '.$a.'<br />$b is '.$b;
?>

Output:

$a is 1
$b is 2
--------------
$a is 2
$b is 1
--------------
$a is 1
$b is 2
27-Apr-2006 12:31
[Editors note: This is not true. You will have to check every possible combination, for instance: $a = array(); $b = ""; $c = null; $d = 0; will print "not equal" but if you set $b = null and $c = "" it will print "equal"]

For the sake of completeness I want to note that a check for equality doesn't have so long winded as in the (wrong) example below. Because of simple laws of logic you don't have to check every possible combination of the variables. The following code will be no surprise for most of you, but for some people it could be helpful.
-Tim

<?php
$a
= 42 ;
$b = 42 ;
$c = 1138 ;
$d = 1701 ;

echo
$a==$b && $b==$c && $c==$d ? "equal" : "not equal" ; // echoes not equal

$a = 42 ;
$b = 42 ;
$c = 42 ;
$d = 42 ;

echo
$a==$b && $b==$c && $c==$d ? "equal" : "not equal" ; // echoes equal
?>
Xavier Daull
04-Feb-2006 01:34
<?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);
}

?>
John L
02-Feb-2006 02:11
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.
Nina Cording
29-Oct-2005 09:50
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);
}

?>
zlel grxnslxves13 at hotmail dot com~=s/x/ee/g
26-Oct-2005 07:30
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
zlel
26-Oct-2005 05:22
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;
}
Tbrendstrup
29-Sep-2005 05:23
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
junk at spheresys dot com
21-Sep-2005 11:25
I wanted to print a binary representation of an integer here it is

<?php

function binString($var, $safety = 0) {   
    return (
$var & 1) . ($var != 0 && $safety < 31 ? binString($var >> 1, $safety+1) : "");
}

print
binString(5); // would print 101
print binString(15); // would print 1111

?>

Explanation:
the first expression after the return statement will evaluate to 1 or 0 according to the least significant bit.

then we have our stop condition if there is still non zero bits left or if we are at the 32nd bit otherwise we call recursively on to the n-1 bits (by shifting out the least significant bit).
Planev ea2005#planev.com
02-Sep-2005 06:42
My very simple bit functions:
setbit($bits,$bit)
getbit($bits,$bit)
unsetbit($bits,$bit)

function setbit($bits,$i){
 
 if (($i <= strlen($bits)-1) && ($i >= 0)) {
  $bits[strlen($bits)-1-$i] = "1";
 }
 return $bits;  
}

function unsetbit($bits,$i){
 
 if (($i <= strlen($bits)-1) && ($i >= 0)) {
  $bits[strlen($bits)-1-$i] = "0";
 }
 return $bits;  
}

function getbit($bits,$i){
 
 if (($i <= strlen($bits)-1) && ($i >= 0)) {
  $bit = $bits[strlen($bits)-1-$i];
 }
 return $bit;
}
Eric Swanson
31-Aug-2005 05:19
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.

/*
not having much experience with bitwise operations, I cannot tell you that this is the BEST solution, but it certainly is a solution that finally works and always returns the EXACT same result Perl provides.
*/
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 />");
alexrussell101 at gmail dot com
24-Jul-2005 04:28
re the 12 ^ 9 = 5 comment, the link doesn't seem to work but here's the explanation (although if you're dealing with bitwise operators you really should get this):

12 in binary is 1100
9 in binary is 1001

If you XOR (the ^ operator) these two binary values you get the binary value 0101 (101) which is 5 in decimal. It's that simple.
redduck666 at gmail dot com
23-Jul-2005 03:26
since i have no idea how do we get:

echo 12 ^ 9; // Outputs '5'

i searched for another tutorial, and this is what i found http://www.litfuel.net/tutorials/bitwise.htm, hopefully someone will find this useful ;)
S
Новости
11 июля 2007
Сайт запущен
© 2007 info@grandviewstudio.com
Z058440144362 Z348613067571