⚡ Truques Bitwise em PHP

Coleção de one-liners e hacks práticos usando operações bitwise.


🔢 Verificações Rápidas

Verificar se número é par

<?php
$isPar = ($n & 1) === 0;

// Exemplos:
var_dump(4 & 1 === 0);  // true  (par)
var_dump(7 & 1 === 0);  // false (ímpar)

Verificar se número é ímpar

<?php
$isImpar = ($n & 1) === 1;

// Ou simplesmente:
$isImpar = $n & 1;  // Retorna 1 (truthy) se ímpar, 0 (falsy) se par

Verificar se número é potência de 2

<?php
$isPowerOf2 = ($n > 0) && (($n & ($n - 1)) === 0);

// Exemplos:
// 8:  1000 & 0111 = 0000 ✓
// 16: 10000 & 01111 = 00000 ✓
// 6:  0110 & 0101 = 0100 ✗

Verificar se dois números têm sinais opostos

<?php
$oppositeSign = ($a ^ $b) < 0;

// Se XOR for negativo, têm sinais diferentes
var_dump((5 ^ -3) < 0);   // true
var_dump((5 ^ 3) < 0);    // false

🎯 Manipulação de Bits

Obter bit em posição específica

<?php
function getBit($num, $pos) {
    return ($num >> $pos) & 1;
}

// Exemplo: obter 3º bit de 13 (1101)
echo getBit(13, 2);  // 1

// Saída esperada
// 1

Setar bit em posição específica (torná-lo 1)

<?php
function setBit($num, $pos) {
    return $num | (1 << $pos);
}

// Exemplo: setar 2º bit de 8 (1000) → 12 (1100)
echo setBit(8, 2);  // 12

// Saída esperada
// 12

Limpar bit em posição específica (torná-lo 0)

<?php
function clearBit($num, $pos) {
    return $num & ~(1 << $pos);
}

// Exemplo: limpar 3º bit de 15 (1111) → 7 (0111)
echo clearBit(15, 3);  // 7

// Saída esperada
// 7

Alternar bit em posição específica (flip)

<?php
function toggleBit($num, $pos) {
    return $num ^ (1 << $pos);
}

// Exemplo: toggle 2º bit de 12 (1100) → 8 (1000)
echo toggleBit(12, 2);  // 8

// Saída esperada
// 8

Contar quantos bits estão setados (população de bits)

<?php
function popCount($n) {
    $count = 0;
    while ($n) {
        $count += $n & 1;
        $n >>= 1;
    }
    return $count;
}

// Método Brian Kernighan (mais eficiente)
function popCountFast($n) {
    $count = 0;
    while ($n) {
        $n &= $n - 1;  // Remove o bit 1 mais à direita
        $count++;
    }
    return $count;
}

echo popCount(13);      // 3 (1101 tem três bits 1)
echo popCountFast(15);  // 4 (1111 tem quatro bits 1)

// Saída esperada
// 34

🚀 Otimizações Matemáticas

Multiplicar por potência de 2

<?php
$multiply2 = $n << 1;   // $n * 2
$multiply4 = $n << 2;   // $n * 4
$multiply8 = $n << 3;   // $n * 8
$multiply16 = $n << 4;  // $n * 16

// Exemplo:
echo 5 << 3;  // 40 (5 * 8)

Dividir por potência de 2 (divisão inteira)

<?php
$divide2 = $n >> 1;   // floor($n / 2)
$divide4 = $n >> 2;   // floor($n / 4)
$divide8 = $n >> 3;   // floor($n / 8)

// Exemplo:
echo 20 >> 2;  // 5 (20 / 4)

Obter módulo de potência de 2

<?php
// $n % $powerOf2 pode ser otimizado
$mod2 = $n & 1;      // $n % 2
$mod4 = $n & 3;      // $n % 4
$mod8 = $n & 7;      // $n % 8
$mod16 = $n & 15;    // $n % 16

// Exemplo:
echo 13 & 7;  // 5 (13 % 8)

Trocar sinal (negativo ↔ positivo)

<?php
$opposite = ~$n + 1;

// Exemplos:
echo ~5 + 1;   // -5
echo ~(-5) + 1;  // 5

Valor absoluto sem condicionais

<?php
function abs($n) {
    $mask = $n >> 31;  // -1 se negativo, 0 se positivo
    return ($n + $mask) ^ $mask;
}

// Ou mais simples (funciona apenas em 32-bit):
function absFast($n) {
    $mask = $n >> (PHP_INT_SIZE * 8 - 1);
    return ($n ^ $mask) - $mask;
}

Máximo de dois números sem condicionais

<?php
function max($a, $b) {
    return $a ^ (($a ^ $b) & -($a < $b));
}

Mínimo de dois números sem condicionais

<?php
function min($a, $b) {
    return $b ^ (($a ^ $b) & -($a < $b));
}

🔄 Truques de Troca

Trocar dois valores sem variável temporária

<?php
// Método 1: XOR
$a = $a ^ $b;
$b = $a ^ $b;
$a = $a ^ $b;

// Método 2: Soma/Subtração (cuidado com overflow)
$a = $a + $b;
$b = $a - $b;
$a = $a - $b;

Trocar nibbles (4 bits) de um byte

<?php
function swapNibbles($n) {
    return (($n & 0x0F) << 4) | (($n & 0xF0) >> 4);
}

// Exemplo: 0x3A (0011 1010) → 0xA3 (1010 0011)
printf("0x%X\n", swapNibbles(0x3A));  // 0xA3

// Saída esperada
// 0xA3

🎨 Máscaras e Flags

Verificar múltiplas flags de uma vez

<?php
define('FLAG_A', 1);
define('FLAG_B', 2);
define('FLAG_C', 4);

$flags = FLAG_A | FLAG_C;  // 0101

// Verificar se TEM Flag A
$hasA = ($flags & FLAG_A) !== 0;  // true

// Verificar se TEM Flag A E Flag C
$hasAC = ($flags & (FLAG_A | FLAG_C)) === (FLAG_A | FLAG_C);  // true

// Verificar se TEM pelo menos uma das flags
$hasAnyAC = ($flags & (FLAG_A | FLAG_C)) !== 0;  // true

Setar múltiplas flags

<?php
$flags |= FLAG_A | FLAG_B;  // Adiciona FLAG_A e FLAG_B

Remover múltiplas flags

<?php
$flags &= ~(FLAG_A | FLAG_B);  // Remove FLAG_A e FLAG_B

Toggle múltiplas flags

<?php
$flags ^= FLAG_A | FLAG_B;  // Alterna FLAG_A e FLAG_B

🎯 Casos de Uso Práticos

RGB para Hexadecimal e vice-versa

<?php
// RGB para HEX
function rgbToHex($r, $g, $b) {
    return ($r << 16) | ($g << 8) | $b;
}

// HEX para RGB
function hexToRgb($hex) {
    return [
        'r' => ($hex >> 16) & 0xFF,
        'g' => ($hex >> 8) & 0xFF,
        'b' => $hex & 0xFF
    ];
}

// Exemplo:
$hex = rgbToHex(255, 128, 64);      // 0xFF8040
$rgb = hexToRgb(0xFF8040);          // ['r'=>255, 'g'=>128, 'b'=>64]

Arredondar para próxima potência de 2

<?php
function nextPowerOf2($n) {
    $n--;
    $n |= $n >> 1;
    $n |= $n >> 2;
    $n |= $n >> 4;
    $n |= $n >> 8;
    $n |= $n >> 16;
    return $n + 1;
}

echo nextPowerOf2(10);  // 16
echo nextPowerOf2(33);  // 64

// Saída esperada
// 1664

Inverter bits de um número

<?php
function reverseBits($n, $bits = 8) {
    $result = 0;
    for ($i = 0; $i < $bits; $i++) {
        $result = ($result << 1) | ($n & 1);
        $n >>= 1;
    }
    return $result;
}

// Exemplo: 10 (00001010) → 80 (01010000) em 8 bits
echo reverseBits(10, 8);  // 80

// Saída esperada
// 80

Encontrar bit diferente entre dois números

<?php
function findDifferentBit($a, $b) {
    $xor = $a ^ $b;
    return log($xor & -$xor, 2);  // Posição do bit diferente
}

// Exemplo: 5 (0101) vs 7 (0111)
echo findDifferentBit(5, 7);  // 1 (segundo bit é diferente)

// Saída esperada
// 1

🧮 Truques Avançados

Encontrar número que aparece uma vez (todos os outros aparecem duas vezes)

<?php
function findSingle($arr) {
    $result = 0;
    foreach ($arr as $num) {
        $result ^= $num;  // XOR cancela pares
    }
    return $result;
}

echo findSingle([2, 3, 5, 4, 5, 3, 4]);  // 2

// Saída esperada
// 2

Detectar se inteiro tem sinais alternados em binário

<?php
function hasAlternatingBits($n) {
    $xor = $n ^ ($n >> 1);
    return ($xor & ($xor + 1)) === 0;
}

// Exemplos:
var_dump(hasAlternatingBits(5));   // true  (101)
var_dump(hasAlternatingBits(10));  // true  (1010)
var_dump(hasAlternatingBits(7));   // false (111)

// Saída esperada
// bool(true)
// bool(true)
// bool(false)

Transformar maiúscula em minúscula e vice-versa

<?php
// ASCII: 'A' = 65 (01000001), 'a' = 97 (01100001)
// Diferença está no 6º bit (32 = 0x20)

$lowercase = chr(ord('A') | 0x20);   // 'a'
$uppercase = chr(ord('a') & ~0x20);  // 'A'

// Toggle case:
$toggled = chr(ord('A') ^ 0x20);  // 'a'

💡 Dicas de Performance

<?php
// ✅ RÁPIDO: Usar shift para multiplicar/dividir por potências de 2
$fast = $n << 3;  // $n * 8

// ❌ LENTO: Multiplicação convencional
$slow = $n * 8;

// ✅ RÁPIDO: Usar AND para módulo de potência de 2
$fast = $n & 7;  // $n % 8

// ❌ LENTO: Módulo convencional
$slow = $n % 8;

// ✅ RÁPIDO: Verificar par/ímpar
$fast = $n & 1;

// ❌ LENTO: Módulo
$slow = $n % 2;

⚠️ Armadilhas Comuns

<?php
// ❌ CUIDADO: Precedência de operadores
$wrong = $n & 1 === 0;  // Avalia (1 === 0) primeiro!
$correct = ($n & 1) === 0;  // ✅ Correto

// ❌ CUIDADO: Overflow em deslocamentos
$overflow = 1 << 64;  // Comportamento indefinido!

// ❌ CUIDADO: NOT (~) em números negativos
$weird = ~-1;  // Resultado pode surpreender!

🎓 Cheat Sheet

<?php
// Verificações
$n & 1              // Par? (0 = par, 1 = ímpar)
($n & ($n-1)) === 0 // É potência de 2?

// Operações rápidas
$n << k             // Multiplica por 2^k
$n >> k             // Divide por 2^k
$n & (2^k - 1)      // Módulo por 2^k

// Manipulação
$n | (1 << k)       // Seta bit k
$n & ~(1 << k)      // Limpa bit k
$n ^ (1 << k)       // Toggle bit k
($n >> k) & 1       // Lê bit k

// Truques
$a ^ $b             // Troca sem temp
~$n + 1             // Troca sinal
$n & -$n            // Isola bit mais à direita

🔗 Recursos Relacionados

  • Ver também: Operações Bitwise em PHP para explicações detalhadas
  • Tags relacionadas: performance, otimização, flags, permissões

Lembre-se: bitwise é poderoso, mas priorize legibilidade sobre micro-otimizações prematuras!