Ir para o conteúdo principal

Lógica em nível de bits: operadores bitwise e máscaras em Python

Autor
Francisco Bustamante
Químico, cientista de dados e programador Python.
Tabela de conteúdos
Do Zero ao Float - Este artigo faz parte de uma série de artigos.
Parte 4: Esse Artigo

Nos artigos anteriores, exploramos a aritmética dos computadores. Aprendemos como eles somam, subtraem usando Complemento de Dois e como lidam com os limites físicos de memória (Overflow). No artigo anterior, usamos brevemente o operador & para revelar a representação interna dos números negativos em Python. Se você ficou curioso sobre o que aquele & realmente fez — e o que mais é possível fazer com ele —, chegou a hora de descobrir.

Nesses casos aritméticos, os bits eram tratados em conjunto para representar um valor numérico (peso posicional). Mas, às vezes, não queremos calcular o valor de um número. Queremos manipular a estrutura dele. Queremos acender uma “chave” específica, desligar um sensor, criptografar uma mensagem ou verificar permissões de um usuário.

É hora de deixar a aritmética de um pouco de lado e entrar no mundo da Lógica Bitwise (Bit a Bit). Aqui, o processador não enxerga o número “5” ou “10”, ele enxerga uma sequência de interruptores individuais que podem ser ligados (1) ou desligados (0).

A Conexão com a Lógica Proposicional
#

Se você acompanha o blog, já deve ter lido nosso artigo sobre Lógica para Programadores. Lá, vimos como criar tabelas-verdade para decidir se uma proposição complexa é Verdadeira (V) ou Falsa (F).

A lógica bitwise é a aplicação direta desse conceito, mas em escala industrial:

  • Verdadeiro (V) vira 1.
  • Falso (F) vira 0.

Em vez de comparar apenas duas proposições (“A porta está aberta” E “A luz está acesa”), o operador bitwise compara 8, 16, 32 ou 64 pares de proposições de uma só vez, em paralelo.

O Grande Alerta: and não é &
#

Antes de vermos os operadores, precisamos resolver uma confusão clássica em Python, linguagem mais adotada aqui no blog. Diferente de linguagens como C ou Java, Python é muito verbal, usando palavras como and, or e not. Porém, ele também possui os símbolos &, | e ~.

Eles não são a mesma coisa.

No artigo sobre Curto-Circuito em Python, explicamos que os operadores lógicos (and, or) são “preguiçosos”. Eles param de avaliar assim que descobrem a resposta.

Já os operadores Bitwise (&, |) são “trabalhadores incansáveis”. Eles avaliam todos os bits, sem exceção, e geram um novo número como resultado.

Tabela de Diferenças
#

Característica Operadores Lógicos Operadores Bitwise
Alvo Expressões Booleanas (True/False) ou objetos Números Inteiros (Bits individuais)
Avaliação Curto-Circuito (Lazy evaluation) Completa (Eager evaluation)
Resultado Retorna um dos operandos (ex: True) Retorna um novo número inteiro
Exemplo if idade > 18 and tem_cnh: masked = flags & 0xFF
Cuidado no Python

Jamais use & no lugar de and dentro de um if, a menos que você saiba exatamente o que está fazendo.

  • 5 and 6 retorna 6 (pois 5 é verdadeiro, ele segue e retorna o último).
  • 5 & 6 retorna 4 (pois faz a interseção dos bits de 101 e 110).

São comportamentos completamente diferentes que podem gerar bugs silenciosos.

Agora que separamos a lógica de fluxo da lógica de bits, vamos conhecer as quatro ferramentas fundamentais: AND, OR, XOR e NOT.

A Matemática dos Bits: AND e OR
#

Para entender bitwise, precisamos parar de olhar para o número decimal (ex: 12) e começar a olhar para a sua representação binária. As operações acontecem verticalmente, alinhando os bits correspondentes de cada número.

AND (&) - A Interseção
#

O operador E Bit a Bit (Bitwise AND) segue uma regra estrita: o bit de resultado será 1 se, e somente se, os bits de ambos os operandos forem 1.

Pense no AND como um Filtro ou uma Interseção.

  • 1 & 1 = 1
  • 1 & 0 = 0
  • 0 & 1 = 0
  • 0 & 0 = 0

Observe que o Zero é dominante: qualquer coisa combinada com zero vira zero. Essa propriedade torna o AND a ferramenta perfeita para desligar bits ou verificar valores (técnica conhecida como Masking).

Exemplo Prático: 12 & 10

Vamos operar 12 & 10.

  • 12 em binário é 1100.
  • 10 em binário é 1010.

Alinhamos os bits e aplicamos a lógica coluna por coluna:

$$ \begin{array}{r c c c c l} & 1 & 1 & 0 & 0 & (12) \\ \& & 1 & 0 & 1 & 0 & (10) \\ \hline & 1 & 0 & 0 & 0 & (8) \end{array} $$

Análise:

  • Bit 3 (esquerda): 1 & 1 resulta em 1.
  • Bit 2: 1 & 0 resulta em 0.
  • Bit 1: 0 & 1 resulta em 0.
  • Bit 0: 0 & 0 resulta em 0.

Resultado: 1000 (que é 8 em decimal).

No Python:

print(12 & 10)        # 8
print(bin(12 & 10))   # 0b1000

OR (|) - A União
#

O operador OU Bit a Bit (Bitwise OR) é mais permissivo: o bit de resultado será 1 se pelo menos um dos bits dos operandos for 1.

Pense no OR como uma Soma Lógica ou União.

  • 1 | 1 = 1
  • 1 | 0 = 1
  • 0 | 1 = 1
  • 0 | 0 = 0

Aqui, o Um é dominante: qualquer coisa combinada com 1 vira 1. Essa propriedade torna o OR a ferramenta ideal para ligar bits (forçar um valor a 1) sem alterar os outros.

Exemplo Prático: 12 | 10

Vamos operar 12 | 10.

  • 12 em binário é 1100.
  • 10 em binário é 1010.

Alinhamos os bits e aplicamos a lógica coluna por coluna:

$$ \begin{array}{r c c c c l} & 1 & 1 & 0 & 0 & (12) \\ \| & 1 & 0 & 1 & 0 & (10) \\ \hline & 1 & 1 & 1 & 0 & (14) \end{array} $$

Análise:

  • Onde havia pelo menos um bit 1 nas entradas, a saída foi 1.
  • Apenas a última coluna (0 | 0) resultou em zero.

Resultado: 1110 (que é 14 em decimal).

No Python:

print(12 | 10)        # 14
print(bin(12 | 10))   # 0b1110

Resumo da Lógica
#

Para não esquecer:

  • AND (&): Usado para Cortar/Limpar. Se você quer garantir que certos bits sejam zero, use AND com 0.
  • OR (|): Usado para Adicionar/Ligar. Se você quer garantir que certos bits sejam um, use OR com 1.

Na próxima seção, veremos outros operadores: o XOR (o localizador de diferenças) e o NOT (a inversão total).

Os Operadores XOR e NOT
#

Enquanto AND e OR são intuitivos (parecem interseção e união de conjuntos), os próximos dois operadores possuem comportamentos únicos que são a base da criptografia e da aritmética de inteiros.

XOR (^) - O Detetive de Diferenças
#

O OU Exclusivo (Exclusive OR, ou XOR) é um operador comum em criptografia e algoritmos de hash. A regra é: o bit de resultado será 1 se os bits dos operandos forem diferentes.

  • 1 ^ 1 = 0 (Iguais \(\to\) 0)
  • 0 ^ 0 = 0 (Iguais \(\to\) 0)
  • 1 ^ 0 = 1 (Diferentes \(\to\) 1)
  • 0 ^ 1 = 1 (Diferentes \(\to\) 1)

Pense no XOR como um Alternador (Toggle).

  • Se você faz XOR com 0, o bit original se mantém (1^0=1, 0^0=0).
  • Se você faz XOR com 1, o bit original se inverte (1^1=0, 0^1=1).

Sua utilidade em criptografia vem de uma propriedade bem mais forte do que “há tantos 0s quanto 1s no resultado”: se o bit da chave for uniformemente aleatório, o bit de saída é uniformemente aleatório, não importa qual fosse o bit da mensagem. Olhar para o resultado não diz nada sobre o texto original — nem um pouquinho. Esse é o argumento do one-time pad, o único esquema de cifra com sigilo perfeito demonstrado, resultado provado por Claude Shannon em 1949.

Exemplo Prático: 12 ^ 10

Vamos operar 12 ^ 10.

$$ \begin{array}{r c c c c l} & 1 & 1 & 0 & 0 & (12) \\ \text{\textasciicircum} & 1 & 0 & 1 & 0 & (10) \\ \hline & 0 & 1 & 1 & 0 & (6) \end{array} $$

Análise:

  • Bit 3: 1 e 1 são iguais \(\to\) 0.
  • Bit 2: 1 e 0 são diferentes \(\to\) 1.
  • Bit 1: 0 e 1 são diferentes \(\to\) 1.
  • Bit 0: 0 e 0 são iguais \(\to\) 0.

Resultado: 0110 (que é 6 em decimal).

Curiosidade Criptográfica: O XOR é reversível. Se você calcular (A ^ B) ^ B, o resultado volta a ser A.

chave = 123
msg = 456

cripto = msg ^ chave
print(cripto)          # 435

print(cripto ^ chave)  # 456 — o XOR com a mesma chave desfaz a operação
Um cifrador XOR caseiro não protege nada

O sigilo perfeito do parágrafo anterior vale sob duas condições rígidas: a chave precisa ser aleatória, tão longa quanto a mensagem, e nunca pode ser reutilizada. Um cifrador XOR com uma chave curta repetida ao longo do texto — que é o que quase todo mundo escreve na primeira tentativa — cai por análise de frequência, e sem precisar adivinhar a chave: posições separadas por um múltiplo do tamanho da chave foram cifradas com o mesmo byte, o que devolve o problema a uma cifra de substituição simples. Ótimo exercício de bits; péssima escolha para proteger qualquer coisa de verdade.

Essa reversibilidade rende um truque clássico: trocar o conteúdo de duas variáveis sem usar uma terceira.

a, b = 5, 9

a ^= b   # a guarda agora a "diferença" entre os dois
b ^= a   # b recebe o valor original de a
a ^= b   # a recebe o valor original de b

print(a, b)   # 9 5

Funciona, mas vale mais como demonstração da propriedade do que como técnica. Em Python, a, b = b, a é mais claro e mais rápido. E o truque esconde uma armadilha: se os dois nomes apontarem para a mesma posição (lista[i] e lista[j] com i == j), o primeiro XOR zera o valor e ele não volta nunca mais.

NOT (~) - A Inversão Total
#

O operador NÃO Bit a Bit (Bitwise NOT) é o único que opera sobre um único número (operador unário). Ele simplesmente inverte todos os bits: o que é 0 vira 1, e o que é 1 vira 0.

Visualmente, parece simples. Mas se você testar no Python, o resultado pode te confundir:

print(~0)    # -1
print(~10)   # -11

Por que o resultado é negativo?

Aqui entra o conhecimento do nosso artigo anterior sobre Complemento de Dois.

  1. O Python trata inteiros como se tivessem “infinitos bits” à esquerda. O número 10 positivo é ...00001010.
  2. Quando aplicamos o NOT (~), invertemos todos os bits, inclusive os infinitos zeros à esquerda. O número vira ...11110101.
  3. Uma sequência infinita de 1s à esquerda indica um número negativo.

Matematicamente, em sistemas de Complemento de Dois, o operador ~x é equivalente ao Complemento de 1. A relação com o valor decimal segue a fórmula:

Entendendo a Fórmula (~10)

Queremos calcular ~10.

  1. Pela fórmula matemática: -(10) - 1 = -11.
  2. Pela visão de bits (em 8 bits para simplificar):
  • 10 é 0000 1010.

  • Invertendo tudo (~): 1111 0101.

  • O que é 1111 0101? Como o primeiro bit é 1, é negativo.

  • Para descobrir o valor, aplicamos a regra inversa (Inverte e soma 1):

  • Inverte: 0000 1010 (10)

  • Soma 1: 0000 1011 (11)

  • Logo, o valor original era -11.

No próximo tópico, veremos como mover esses bits de um lado para o outro com os operadores de Shift (Deslocamento).

Shift: A Aritmética do Deslocamento
#

Imagine que você tem o número decimal 23. Se você adicionar um zero à direita (230), você efetivamente multiplicou o número por 10 (a base do sistema). Se remover o último dígito (o 3), você dividiu por 10 (divisão inteira, restando 2).

No sistema binário, a lógica é idêntica, mas a base é 2. Mover os bits para a esquerda ou direita é a forma mais eficiente computacionalmente de multiplicar ou dividir números.

Left Shift (<<) - O Multiplicador
#

O operador Deslocamento à Esquerda (x << n) move todos os bits de x para a esquerda por n posições.

  • Os bits que “caem” pela esquerda são descartados (em tipos de tamanho fixo) ou o número cresce (em Python).
  • Zeros são inseridos nas posições vagas à direita.

Regra Matemática: Deslocar n bits para a esquerda equivale a multiplicar o número por \(2^n\).

$$ x \ll n \iff x \times 2^n $$
Exemplo: 3 « 2

O número 3 em binário (8 bits) é 0000 0011. Vamos deslocar 2 casas para a esquerda.

  1. Empurre tudo para a esquerda.
  2. Preencha os buracos da direita com 0. $$ \begin{array}{r c c l} & 0000 & 0011 & (3) \\ \downarrow & \text{Shift Left 2} & & \\ & 0000 & 11\mathbf{00} & (12) \end{array} $$ Verificação: \(3 \times 2^2 = 3 \times 4 = 12\).

Right Shift (>>) - O Divisor
#

O operador Deslocamento à Direita (x >> n) move os bits para a direita.

  • Os bits da direita “caem” no abismo e são perdidos.
  • O que entra pela esquerda? Aqui está o grande detalhe técnico.

Em linguagens de baixo nível, existem dois tipos de shift à direita:

  1. Logical Shift: Preenche sempre com 0. Usado para números sem sinal (Unsigned).
  2. Arithmetic Shift: Preenche com o valor do Bit de Sinal (o bit mais à esquerda). Se o número era negativo (começava com 1), ele preenche com 1s para manter o número negativo.

Como o Python faz? Como Python lida com números com sinal e precisão arbitrária, o operador >> se comporta como um Arithmetic Shift. Ele preserva o sinal matemático.

Regra Matemática: Deslocar n bits para a direita equivale a uma Divisão Inteira (Floor Division) por \(2^n\).

$$ x \gg n \iff \lfloor x / 2^n \rfloor $$

Com os dois operadores apresentados, vale ver os dois movimentos desenhados lado a lado. Repare no que entra e no que sai em cada caso — é essa assimetria que explica por que um deles é reversível e o outro não:

Dois painéis mostrando deslocamentos de bits. No painel de cima, o left shift de 3 por duas casas: os oito bits 00000011 se movem duas posições para a esquerda, dois zeros caem pela borda esquerda, em caixas tracejadas marcadas como caem no abismo, e dois zeros novos, em amarelo, entram pela direita, resultando em 00001100, que vale 12. No painel de baixo, o right shift de 13 por duas casas: os bits 00001101 se movem duas posições para a direita, os dois últimos bits, 0 e 1, caem pela borda direita em caixas tracejadas, e dois zeros novos entram pela esquerda, resultando em 00000011, que vale 3. Uma nota explica que os bits que caem são o resto da divisão, e é por isso que 13 deslocado duas casas para a direita dá 3, e não 3,25.
Deslocar para a esquerda só cria zeros; deslocar para a direita descarta bits — e o que foi descartado é exatamente o resto da divisão.

O << empurra zeros para dentro e, enquanto houver espaço, nada se perde. Já o >> joga fora os bits da ponta direita, e esses bits eram justamente o resto da divisão. É por isso que o deslocamento à direita equivale a uma divisão inteira: o resto não vai para lugar nenhum, ele some.

Exemplo Positivo vs. Negativo

Caso 1: Positivo (12 » 2) Binário: 0000 1100 Desloca direita, entra 0: 0000 0011 Resultado: 3. (Matemática: \(12 / 4 = 3\)).

Caso 2: Negativo (-12 » 2) Binário (Complemento de 2): ...1111 0100 Desloca direita. Como o bit de sinal é 1, entram 1s pela esquerda. Resultado: ...1111 1101

O resultado é -3. (Matemática: \(-12 / 4 = -3\)).

Isso garante que a divisão por potências de 2 funcione corretamente tanto para números positivos quanto negativos.

>> é piso, não truncamento

O exemplo acima passa a impressão de que >> e “dividir por 4” são a mesma coisa, porque \(-12\) é múltiplo de 4 e o resultado sai redondo. Troque o número por \(-13\) e a diferença aparece: -13 >> 2 devolve -4, enquanto uma divisão que trunca em direção ao zero — como o / entre inteiros em C — devolveria -3.

O motivo está na própria fórmula \(\lfloor x / 2^n \rfloor\): o piso arredonda para baixo, e para números negativos “para baixo” é para longe do zero. Em Python isso é coerente com o //, que também é piso (-13 // 4 é -4). Se você precisa mesmo do truncamento, int(-13 / 4) devolve -3.

Na próxima seção, vamos juntar tudo o que aprendemos (AND, OR, NOT, SHIFT) para criar ferramentas práticas chamadas Máscaras de Bits (Bitmasks).

Máscaras de Bits (Bitmasks)
#

Agora que conhecemos os operadores, podemos combiná-los para realizar alterações precisas em números. A técnica de usar um número auxiliar para “selecionar” ou “modificar” bits específicos de outro número é chamada de Mascaramento (Masking).

Para os exemplos abaixo, imagine que queremos manipular o 3º bit (da direita para esquerda, índice 2). Para isso, criamos uma máscara onde apenas esse bit é 1:

MASK = 1 << 2  # Resultado: 0b00000100 (Decimal 4)

Consultar um Bit (Check)
#

Queremos saber: “O 3º bit está ligado?”. Usamos o AND (&). Como o AND com 0 zera tudo, se o resultado for diferente de zero, significa que o nosso bit alvo estava ligado.

flags = 0b10110111
is_on = (flags & MASK) != 0
# O resultado será True se o 3º bit estiver ligado (que é o caso), False caso contrário.

Repare no != 0, e não > 0. Com os valores deste exemplo os dois funcionam, mas o artigo anterior passou boa parte do tempo mostrando que inteiros podem ser negativos — e, se flags for negativo (algo que o ~ produz com facilidade), o resultado do & pode ser negativo também, e um teste > 0 responderia “bit desligado” para um bit que está ligado. Comparar com zero por diferença é sempre seguro.

Ligar um Bit (Set)
#

Queremos forçar o 3º bit a ser 1, sem mexer nos outros. Usamos o OR (|). Como o OR com 0 preserva o valor original, e o OR com 1 força virar 1, nossa máscara protege o resto e acende o alvo.

flags = flags | MASK 
# Agora o 3º bit é 1 com certeza. Os outros não mudaram.

Desligar um Bit (Clear)
#

Queremos forçar o 3º bit a ser 0. Esta é a operação mais complexa logicamente. Precisamos de um AND (para zerar), mas precisamos de uma máscara que tenha 0 no alvo e 1 em todo o resto (para preservar os vizinhos). Como geramos isso? Invertendo nossa máscara original com NOT (~).

A Lógica do Clear (Limpar Bit)
  1. Máscara Original (MASK): ...0000 0100
  2. Máscara Invertida (~MASK): ...1111 1011
  3. Operação (flags & ~MASK): ...10110011

O bit alvo foi zerado, os outros foram preservados pelos 1s da máscara.

flags = flags & (~MASK)

Alternar um Bit (Toggle)
#

Queremos inverter o estado atual: se estava ligado, desliga; se estava desligado, liga. Usamos o XOR (^), pois XOR com 1 sempre inverte o bit.

flags = flags ^ MASK

As quatro operações formam um conjunto fechado, e é útil vê-las juntas depois de tê-las visto uma a uma. A figura abaixo repete o mesmo bit alvo nos quatro casos, com a coluna dele destacada, para que dê para comparar o que cada operador faz com os vizinhos:

Painel com as quatro operações de máscara, dispostas em dois por dois. Em cada uma, três linhas de oito bits: o valor, a máscara e o resultado, com a coluna do bit alvo destacada em amarelo. Consultar usa o operador E com a máscara 00000100 e devolve 00000100, ou seja, resultado diferente de zero, indicando que o bit está ligado. Ligar usa o operador OU e acende o bit alvo sem mexer nos vizinhos. Desligar usa o operador E com a máscara invertida 11111011, zerando o alvo e preservando o resto. Alternar usa o operador OU-exclusivo, que com 1 sempre inverte o bit.
As quatro operações de máscara sobre o mesmo bit alvo: o AND consulta, o OR acende, o AND com a máscara invertida apaga e o XOR inverte.

Repare no padrão: o bit alvo muda em todas, e as outras sete colunas ficam intactas em todas. É esse o ponto do mascaramento — mexer numa posição sem tocar em nenhuma outra.

Duas funções que poupam trabalho
#

Python já traz embutidas duas operações que aparecem o tempo todo em código real de manipulação de bits e que evitam escrever laços na mão:

flags = 0b10110111

flags.bit_count()   # 6  — quantos bits estão ligados (Python 3.10+)
flags.bit_length()  # 8  — quantos bits são necessários para representar o valor

bit_count() é o que a literatura chama de popcount, e responde em uma chamada perguntas como “quantas permissões esse usuário tem?” ou “quantos itens estão marcados?”. bit_length() responde “onde está o bit mais significativo ligado?”, útil para saber quantos bits um valor realmente ocupa.

Se esse tipo de manipulação te interessou, a referência prática do assunto é o Bit Twiddling Hacks, de Sean Eron Anderson — uma coletânea de truques de bits para tarefas que, à primeira vista, pareceriam exigir laços e condicionais.

Exemplo Real: Sistema de Permissões
#

Vamos ver onde isso é usado na prática. Sistemas operacionais (como Linux) usam bits para controlar permissões de arquivos. Vamos simular isso em Python.

Imagine que temos 3 permissões possíveis, representadas por bits:

  • Leitura (READ): bit 2 (valor 4)
  • Escrita (WRITE): bit 1 (valor 2)
  • Execução (EXEC): bit 0 (valor 1)
# Definindo as constantes (MÁSCARAS)
READ  = 0b100  # 4
WRITE = 0b010  # 2
EXEC  = 0b001  # 1

# Cenário 1: Criando um usuário com permissão de Leitura e Execução
# Usamos OR (|) para combinar as permissões
permissao_usuario = READ | EXEC  
print(bin(permissao_usuario))  # Saída: 0b101 (Decimal 5)

# Cenário 2: O usuário pode escrever?
# Usamos AND (&) para verificar
if (permissao_usuario & WRITE):
    print("Acesso de escrita liberado")
else:
    print("Acesso de escrita negado")  # Vai cair aqui

# Cenário 3: Adicionando permissão de escrita dinamicamente
permissao_usuario = permissao_usuario | WRITE
print(bin(permissao_usuario))
# Agora o valor é 0b111 (7) - Permissão total (rwx)

# Cenário 4: Revogando permissão de execução
# Usamos AND com NOT
permissao_usuario = permissao_usuario & (~EXEC)
print(bin(permissao_usuario))
# Agora o valor é 0b110 (6) - Apenas Leitura e Escrita

Perceba como conseguimos armazenar 3 informações booleanas independentes em uma única variável inteira, usando apenas 3 bits de informação.

Aqui vale uma precisão que costuma passar batida: nada disso ocupa “3 bits de memória”. A menor unidade endereçável de memória é o byte, e um int do Python, com toda a maquinaria de precisão arbitrária que carrega, ocupa 28 bytes — sys.getsizeof(5) mostra isso. O que cabe em 3 bits é a informação. A economia real aparece quando esses bits são empacotados aos milhões, em protocolos de rede, formatos de arquivo e estruturas de dados compactas.

De onde vem o 755 do chmod

Os três bits que acabamos de usar — leitura, escrita, execução — são exatamente os do Unix, e agora dá para ver por que o comando de permissões aparece em octal. As permissões de um arquivo são nove bits, em três grupos de três: dono, grupo e outros. E três bits é precisamente o que um dígito octal representa. Ou seja, chmod 755 é a escrita octal de 111 101 101:

Grupo Dígito octal Bits Significado
Dono 7 111 ler, escrever, executar
Grupo 5 101 ler e executar
Outros 5 101 ler e executar

É por isso que o primeiro artigo desta série dedicou uma seção inteira ao octal: ele não é curiosidade histórica, é a notação que torna nove bits de permissão legíveis de relance. Mais detalhes em file-system permissions.

Tabela-Resumo
#

Operação O que faz Exemplo do artigo
& (AND) bit 1 só se ambos forem 1 12 & 10 = 8
| (OR) bit 1 se algum for 1 12 | 10 = 14
^ (XOR) bit 1 se forem diferentes 12 ^ 10 = 6
~ (NOT) inverte todos os bits: \(\sim x = -x - 1\) ~10 = -11
<< desloca à esquerda: multiplica por \(2^n\) 3 << 2 = 12
>> desloca à direita: \(\lfloor x / 2^n \rfloor\) -12 >> 2 = -3, mas -13 >> 2 = -4
and / or operam sobre valores de verdade, não sobre bits 5 and 6 devolve 6, não 4
Consultar bit (flags & MASK) != 0 bit 2 de 0b10110111 está ligado
Ligar bit flags | MASK preserva os vizinhos
Desligar bit flags & ~MASK 0b10110111 & ~0b100 = 0b10110011
Alternar bit flags ^ MASK XOR com 1 sempre inverte
Criar máscara 1 << n 1 << 2 é 0b100
Contar bits ligados x.bit_count() 0b10110111 tem 6
Tamanho em bits x.bit_length() 0b10110111 ocupa 8
Reverter XOR (A ^ B) ^ B == A base do cifrador e da troca sem variável extra

Conclusão
#

Entrar no mundo Bitwise é como olhar para a Matrix: você para de ver apenas os números superficiais e começa a enxergar a estrutura binária que sustenta tudo.

Embora Python seja uma linguagem de alto nível, entender &, |, ^ e << te dá superpoderes para lidar com protocolos de rede, processamento de imagens, criptografia e otimização de algoritmos.

E caso não use Python, esses conceitos são universais e se aplicam a praticamente todas as linguagens de programação.

Agora você sabe que:

  1. Bitwise não é Lógico: & não é and.
  2. Máscaras são a chave: Use AND para ler/limpar e OR para escrever.
  3. Bits são versáteis: Um único inteiro pode guardar dezenas de “interruptores” independentes.

Pronto para praticar? Tente criar um código que criptografa uma string usando XOR e depois descriptografa usando a mesma chave — lembrando do alerta lá de cima: isso é exercício de manipulação de bits, não proteção de verdade.

Com isso, encerramos o capítulo dos números inteiros nesta série. Ao longo dos últimos artigos, vimos como representar, converter, operar e manipular inteiros em diferentes bases — inclusive na estrutura de bits bruta. Agora é hora de dar o próximo salto: como o computador lida com números fracionários? Por que 0.1 + 0.2 não é exatamente 0.3 em Python? A resposta começa com algo que você já domina — conversão de bases —, mas aplicada agora à parte depois da vírgula.

Até a próxima!

Do Zero ao Float - Este artigo faz parte de uma série de artigos.
Parte 4: Esse Artigo

Relacionados