Operadores bit a bit (bitwise)

Operadores bit-a-bit permitem a avaliação e modificação de bits específicos em um tipo inteiro.

Operadores Bit-a-bit
Exemplo Nome Resultado
$a & $b E (AND) Os bits que estão ativos tanto em $a quanto em $b são ativados.
$a | $b OU (OR inclusivo) Os bits que estão ativos em $a ou em $b são ativados.
$a ^ $b XOR (OR exclusivo) Os bits que estão ativos em $a ou em $b, mas não em ambos, são ativados.
~ $a NÃO (NOT) Os bits que estão ativos em $a não são ativados, e vice-versa.
$a << $b Deslocamento à esquerda Desloca os bits de $a $b passos para a esquerda (cada passo significa "multiplica por dois")
$a >> $b Deslocamento à direita Desloca os bits de $a $b passos para a direita (cada passo significa "divide por dois")

O deslocamento de bits no PHP é aritmético. Bits deslocados no lado final são descartados. Deslocamentos a esquerda tem zeros inseridos à direita e o bit de sinal é perdido, e assim o sinal do operando não é preservado. Deslocamentos a direita tem cópias do bit de sinal à esquerda, e assim o sinal do operando é preservado.

Utilize parenteses para garantir a precedência desejada. Por exemplo $a & $b == true avalia primeiro a equivalência e depois a operação de bits, enquanto que ($a & $b) == true avalia primeiro a operação de bits e só depois a equivalência.

Se ambos os operandos de &, | e ^ forem strings então a operação será realizada nos valores ASCII dos caracteres das strings, e o resultado final será uma string. Em todos os outros casos ambos os operandos serão convertidos para inteiros e o resultado será um inteiro.

Se o operando de ~ for uma string então a operação será realizada nos valores ASCII dos caracteres da string, e o resultado será uma string. Nos demais casos o operando e o resultado serão tratados como inteiros.

Ambos os operandos e o resultado para << e >> sempre são tratados como inteiros.

A configuração error_reporting ini utiliza valores posicionados em bits,
fornecendo um exemplo real de ligar ou desligar
bits. Para mostrar todos os erros mas não os alertas
coloque no php.ini:
E_ALL & ~E_NOTICE
      

Isto funciona pois começa com E_ALL:
00000000000000000111011111111111
Depois pegando o valor de E_NOTICE...
00000000000000000000000000001000
... que invertido através de ~:
11111111111111111111111111110111
... é finalmente mesclado via E (&) e assim o valor final
se torna:
00000000000000000111011111110111
      

Outra forma de conseguir esse efeito seria através do XOR (^)
e assim encontrar os bits que estão ativos em apenas um ou no outro:
E_ALL ^ E_NOTICE
      

O error_reporting pode também ser utilizado para demonstrar a ativação de bits.
Para mostrar apenas erros e erros recuperáveis:
E_ERROR | E_RECOVERABLE_ERROR
      

Este processo combina E_ERROR
00000000000000000000000000000001
e
00000000000000000001000000000000
utilizando OU (|)
para pegar os bits ativos de ambos os valores:
00000000000000000001000000000001
      

Exemplo #1 Operações bitwise AND, OR e XOR em inteiros

<?php
/*
 * Ignore essa primeira parte,
 * que é apenas para deixar a impressão mais limpa.
 */

$format '(%1$2d = %1$04b) = (%2$2d = %2$04b)'
        
' %3$s (%4$2d = %4$04b)' "\n";

echo <<<EOH
 ---------     ---------  -- ---------
 result        value      op test
 ---------     ---------  -- ---------
EOH;


/*
 * Agora os exemplos
 */

$values = array(01248);
$test 4;

echo 
"\n Operador de bit E \n";
foreach (
$values as $value) {
    
$result $value $test;
    
printf($format$result$value'&'$test);
}

echo 
"\n Operador de bit OU inlusivo \n";
foreach (
$values as $value) {
    
$result $value $test;
    
printf($format$result$value'|'$test);
}

echo 
"\n Operador de bit OU Exclusivo (XOR) \n";
foreach (
$values as $value) {
    
$result $value $test;
    
printf($format$result$value'^'$test);
}
?>

O exemplo acima irá imprimir:

 ---------     ---------  -- ---------
 result        value      op test
 ---------     ---------  -- ---------
 Operador de bit E
( 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)

 Operador de bit OU inlusivo
( 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)

 Operador de bit OU Exclusivo (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)

Exemplo #2 Operações de bit XOR em strings

<?php
echo 12 9// Imprime '5'

echo "12" "9"// Imprime caracter backspace (ASCII 8)
                 // ('1' (ascii 49)) ^ ('9' (ascii 57)) = #8

echo "hallo" "hello"// Imprime os valores ASCII #0 #4 #0 #0 #0
                        // 'a' ^ 'e' = #4

echo "3"// Imprime 1
              // 2 ^ ((int)"3") == 1

echo "2" 3// Imprime 1
              // ((int)"2") ^ 3 == 1
?>

Exemplo #3 Deslocamento de bits em inteiros

<?php
/*
 * Aqui estão os exemplos.
 */

echo "\n--- MOVENDO BITS A DIREITA EM INTEIROS POSITIVOS ---\n";

$val 4;
$places 1;
$res $val >> $places;
p($res$val'>>'$places'cópia do bit de sinal trocada para a esquerda');

$val 4;
$places 2;
$res $val >> $places;
p($res$val'>>'$places);

$val 4;
$places 3;
$res $val >> $places;
p($res$val'>>'$places'move os bits para fora da direita');

$val 4;
$places 4;
$res $val >> $places;
p($res$val'>>'$places'mesmo resultado que acima; não se pode mover além do 0');


echo 
"\n--- MOVENDO BITS A DIREITA EM INTEIROS NEGATIVOS ---\n";

$val = -4;
$places 1;
$res $val >> $places;
p($res$val'>>'$places'cópia do bit de sinal trocada para a esquerda');

$val = -4;
$places 2;
$res $val >> $places;
p($res$val'>>'$places'move os bits para fora da direita');

$val = -4;
$places 3;
$res $val >> $places;
p($res$val'>>'$places'mesmo resultado que acima; não se pode mover além do -1');


echo 
"\n--- MOVENDO BITS A ESQUERDA EM INTEIROS POSITIVOS ---\n";

$val 4;
$places 1;
$res $val << $places;
p($res$val'<<'$places'preenche com zeros o lado direito');

$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'bit de sinal movido para fora');

$val 4;
$places = (PHP_INT_SIZE 8) - 2;
$res $val << $places;
p($res$val'<<'$places'bits movidos para fora da esquerda');


echo 
"\n--- MOVENDO BITS A ESQUERDA EM INTEIROS NEGATIVOS ---\n";

$val = -4;
$places 1;
$res $val << $places;
p($res$val'<<'$places'preenche com zeros o lado direito');

$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 movidos para fora pela esquerda, incluindo o bit de sinal');


/*
 * Ignore essa seção abaixo,
 * é apenas para formatar a saída e deixar mais clara a apresentação.
 */

function p($res$val$op$places$note '') {
    
$format '%0' . (PHP_INT_SIZE 8) . "b\n";

    
printf("Expressão: %d = %d %s %d\n"$res$val$op$places);

    echo 
" Decimal:\n";
    
printf("  val=%d\n"$val);
    
printf("  res=%d\n"$res);

    echo 
" Binário:\n";
    
printf('  val=' $format$val);
    
printf('  res=' $format$res);

    if (
$note) {
        echo 
" NOTA: $note\n";
    }

    echo 
"\n";
}
?>

O exemplo acima irá imprimir em máquinas 32 bit:


--- MOVENDO BITS A DIREITA EM INTEIROS POSITIVOS ---
Expressão: 2 = 4 >> 1
 Decimal:
  val=4
  res=2
 Binário:
  val=00000000000000000000000000000100
  res=00000000000000000000000000000010
 NOTA: cópia do bit de sinal trocada para a esquerda

Expressão: 1 = 4 >> 2
 Decimal:
  val=4
  res=1
 Binário:
  val=00000000000000000000000000000100
  res=00000000000000000000000000000001

Expressão: 0 = 4 >> 3
 Decimal:
  val=4
  res=0
 Binário:
  val=00000000000000000000000000000100
  res=00000000000000000000000000000000
 NOTA: move os bits para fora da direita

Expressão: 0 = 4 >> 4
 Decimal:
  val=4
  res=0
 Binário:
  val=00000000000000000000000000000100
  res=00000000000000000000000000000000
 NOTA: mesmo resultado que acima; não se pode mover além do 0


--- MOVENDO BITS A DIREITA EM INTEIROS NEGATIVOS ---
Expressão: -2 = -4 >> 1
 Decimal:
  val=-4
  res=-2
 Binário:
  val=11111111111111111111111111111100
  res=11111111111111111111111111111110
 NOTA: cópia do bit de sinal trocada para a esquerda

Expressão: -1 = -4 >> 2
 Decimal:
  val=-4
  res=-1
 Binário:
  val=11111111111111111111111111111100
  res=11111111111111111111111111111111
 NOTA: move os bits para fora da direita

Expressão: -1 = -4 >> 3
 Decimal:
  val=-4
  res=-1
 Binário:
  val=11111111111111111111111111111100
  res=11111111111111111111111111111111
 NOTA: mesmo resultado que acima; não se pode mover além do -1


--- MOVENDO BITS A ESQUERDA EM INTEIROS POSITIVOS ---
Expressão: 8 = 4 << 1
 Decimal:
  val=4
  res=8
 Binário:
  val=00000000000000000000000000000100
  res=00000000000000000000000000001000
 NOTA: preenche com zeros o lado direito

Expressão: 1073741824 = 4 << 28
 Decimal:
  val=4
  res=1073741824
 Binário:
  val=00000000000000000000000000000100
  res=01000000000000000000000000000000

Expressão: -2147483648 = 4 << 29
 Decimal:
  val=4
  res=-2147483648
 Binário:
  val=00000000000000000000000000000100
  res=10000000000000000000000000000000
 NOTA: bit de sinal movido para fora

Expressão: 0 = 4 << 30
 Decimal:
  val=4
  res=0
 Binário:
  val=00000000000000000000000000000100
  res=00000000000000000000000000000000
 NOTA: bits movidos para fora da esquerda


--- MOVENDO BITS A ESQUERDA EM INTEIROS NEGATIVOS ---
Expressão: -8 = -4 << 1
 Decimal:
  val=-4
  res=-8
 Binário:
  val=11111111111111111111111111111100
  res=11111111111111111111111111111000
 NOTA: preenche com zeros o lado direito

Expressão: -2147483648 = -4 << 29
 Decimal:
  val=-4
  res=-2147483648
 Binário:
  val=11111111111111111111111111111100
  res=10000000000000000000000000000000

Expressão: 0 = -4 << 30
 Decimal:
  val=-4
  res=0
 Binário:
  val=11111111111111111111111111111100
  res=00000000000000000000000000000000
 NOTA: bits movidos para fora pela esquerda, incluindo o bit de sinal

O exemplo acima irá imprimir em máquinas 64 bit:


--- MOVENDO BITS A DIREITA EM INTEIROS POSITIVOS ---
Expressão: 2 = 4 >> 1
 Decimal:
  val=4
  res=2
 Binário:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0000000000000000000000000000000000000000000000000000000000000010
 NOTA: cópia do bit de sinal trocada para a esquerda

Expressão: 1 = 4 >> 2
 Decimal:
  val=4
  res=1
 Binário:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0000000000000000000000000000000000000000000000000000000000000001

Expressão: 0 = 4 >> 3
 Decimal:
  val=4
  res=0
 Binário:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0000000000000000000000000000000000000000000000000000000000000000
 NOTA: move os bits para fora da direita

Expressão: 0 = 4 >> 4
 Decimal:
  val=4
  res=0
 Binário:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0000000000000000000000000000000000000000000000000000000000000000
 NOTA: mesmo resultado que acima; não se pode mover além do 0


--- MOVENDO BITS A DIREITA EM INTEIROS NEGATIVOS ---
Expressão: -2 = -4 >> 1
 Decimal:
  val=-4
  res=-2
 Binário:
  val=1111111111111111111111111111111111111111111111111111111111111100
  res=1111111111111111111111111111111111111111111111111111111111111110
 NOTA: cópia do bit de sinal trocada para a esquerda

Expressão: -1 = -4 >> 2
 Decimal:
  val=-4
  res=-1
 Binário:
  val=1111111111111111111111111111111111111111111111111111111111111100
  res=1111111111111111111111111111111111111111111111111111111111111111
 NOTA: move os bits para fora da direita

Expressão: -1 = -4 >> 3
 Decimal:
  val=-4
  res=-1
 Binário:
  val=1111111111111111111111111111111111111111111111111111111111111100
  res=1111111111111111111111111111111111111111111111111111111111111111
 NOTA: mesmo resultado que acima; não se pode mover além do -1


--- MOVENDO BITS A ESQUERDA EM INTEIROS POSITIVOS ---
Expressão: 8 = 4 << 1
 Decimal:
  val=4
  res=8
 Binário:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0000000000000000000000000000000000000000000000000000000000001000
 NOTA: preenche com zeros o lado direito

Expressão: 4611686018427387904 = 4 << 60
 Decimal:
  val=4
  res=4611686018427387904
 Binário:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0100000000000000000000000000000000000000000000000000000000000000

Expressão: -9223372036854775808 = 4 << 61
 Decimal:
  val=4
  res=-9223372036854775808
 Binário:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=1000000000000000000000000000000000000000000000000000000000000000
 NOTA: bit de sinal movido para fora

Expressão: 0 = 4 << 62
 Decimal:
  val=4
  res=0
 Binário:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0000000000000000000000000000000000000000000000000000000000000000
 NOTA: bits movidos para fora da esquerda


--- MOVENDO BITS A ESQUERDA EM INTEIROS NEGATIVOS ---
Expressão: -8 = -4 << 1
 Decimal:
  val=-4
  res=-8
 Binário:
  val=1111111111111111111111111111111111111111111111111111111111111100
  res=1111111111111111111111111111111111111111111111111111111111111000
 NOTA: preenche com zeros o lado direito

Expressão: -9223372036854775808 = -4 << 61
 Decimal:
  val=-4
  res=-9223372036854775808
 Binário:
  val=1111111111111111111111111111111111111111111111111111111111111100
  res=1000000000000000000000000000000000000000000000000000000000000000

Expressão: 0 = -4 << 62
 Decimal:
  val=-4
  res=0
 Binário:
  val=1111111111111111111111111111111111111111111111111111111111111100
  res=0000000000000000000000000000000000000000000000000000000000000000
 NOTA: bits movidos para fora pela esquerda, incluindo o bit de sinal

Aviso

Deslocar inteiros em quantidade maior ou igual ao inteiro longo do sistema é um comportamento indefinido. Em outras palavras, não faça trocas maiores que 31 bits em sistemas de 32 bits, e não faça trocas maiores que 63 bits em sistemas de 64 bits.

Utilize as funções da extensão gmp para manipulações de troca de bit em números maiores que PHP_INT_MAX.

Veja também pack(), unpack(), gmp_and(), gmp_or(), gmp_xor(), gmp_testbit(), gmp_clrbit()