Operação bit a bit e uso


102

Considere este código:

x = 1        # 0001
x << 2       # Shift left 2 bits: 0100
# Result: 4

x | 2        # Bitwise OR: 0011
# Result: 3

x & 1        # Bitwise AND: 0001
# Result: 1

Eu posso entender os operadores aritméticos em Python (e outras linguagens), mas nunca entendi os operadores 'bit a bit' muito bem. No exemplo acima (de um livro sobre Python), eu entendo o deslocamento para a esquerda, mas não os outros dois.

Além disso, para que os operadores bit a bit são realmente usados? Eu gostaria de alguns exemplos.



Respostas:


163

Operadores bit a bit são operadores que trabalham em valores de vários bits, mas conceitualmente um bit por vez.

  • ANDé 1 apenas se ambas as entradas forem 1, caso contrário, é 0.
  • ORé 1 se uma ou ambas as entradas forem 1, caso contrário, é 0.
  • XORé 1 apenas se exatamente uma de suas entradas for 1, caso contrário, é 0.
  • NOT é 1 apenas se sua entrada for 0, caso contrário, é 0.

Muitas vezes, eles podem ser melhor mostrados como tabelas de verdade. As possibilidades de entrada estão no topo e à esquerda, o bit resultante é um dos quatro (dois no caso de NOT, pois tem apenas uma entrada) valores mostrados na interseção das entradas.

AND | 0 1     OR | 0 1     XOR | 0 1    NOT | 0 1
----+-----    ---+----     ----+----    ----+----
 0  | 0 0      0 | 0 1       0 | 0 1        | 1 0
 1  | 0 1      1 | 1 1       1 | 1 0

Um exemplo é se você quiser apenas os 4 bits inferiores de um inteiro, faça o AND com 15 (binário 1111) para:

    201: 1100 1001
AND  15: 0000 1111
------------------
 IS   9  0000 1001

Nesse caso, os bits zero em 15 atuam efetivamente como um filtro, forçando os bits no resultado a também serem zero.

Além disso, >>e <<muitas vezes são incluídos como operadores bit a bit, e eles "deslocam" um valor respectivamente para a direita e para a esquerda por um certo número de bits, jogando fora os bits que rolam da extremidade para a qual você está mudando, e alimentando bits zero no outro final.

Então, por exemplo:

1001 0101 >> 2 gives 0010 0101
1111 1111 << 4 gives 1111 0000

Observe que o deslocamento para a esquerda no Python é incomum, pois não usa uma largura fixa onde os bits são descartados - enquanto muitas linguagens usam uma largura fixa com base no tipo de dados, o Python simplesmente expande a largura para fornecer bits extras. Para obter o comportamento de descarte no Python, você pode seguir um deslocamento para a esquerda com um bit a bit and, como em um valor de 8 bits deslocando quatro bits para a esquerda:

bits8 = (bits8 << 4) & 255

Com isso em mente, outro exemplo de operadores bit a bit é se você tiver dois valores de 4 bits que deseja compactar em um de 8 bits, poderá usar todos os três operadores ( left-shift, ande or):

packed_val = ((val1 & 15) << 4) | (val2 & 15)
  • A & 15operação garantirá que ambos os valores tenham apenas os 4 bits inferiores.
  • A << 4é um deslocamento de 4-bits para a esquerda para se mover val1para a parte superior 4 bits de um valor de 8 bits.
  • O |simplesmente combina esses dois juntos.

Se val1é 7 e val2é 4:

                val1            val2
                ====            ====
 & 15 (and)   xxxx-0111       xxxx-0100  & 15
 << 4 (left)  0111-0000           |
                  |               |
                  +-------+-------+
                          |
| (or)                0111-0100

43

Um uso típico:

| é usado para definir um certo bit para 1

& é usado para testar ou limpar um certo bit

  • Defina um bit (onde n é o número do bit e 0 é o bit menos significativo):

    unsigned char a |= (1 << n);

  • Limpe um pouco:

    unsigned char b &= ~(1 << n);

  • Alternar um pouco:

    unsigned char c ^= (1 << n);

  • Teste um pouco:

    unsigned char e = d & (1 << n);

Veja o caso da sua lista, por exemplo:

x | 2é usado para definir o bit 1 de x1

x & 1é usado para testar se o bit 0 xé 1 ou 0


38

para que os operadores bit a bit são realmente usados? Eu gostaria de alguns exemplos.

Um dos usos mais comuns de operações bit a bit é para analisar cores hexadecimais.

Por exemplo, aqui está uma função Python que aceita uma string como #FF09BEe retorna uma tupla de seus valores de vermelho, verde e azul.

def hexToRgb(value):
    # Convert string to hexadecimal number (base 16)
    num = (int(value.lstrip("#"), 16))

    # Shift 16 bits to the right, and then binary AND to obtain 8 bits representing red
    r = ((num >> 16) & 0xFF)

    # Shift 8 bits to the right, and then binary AND to obtain 8 bits representing green
    g = ((num >> 8) & 0xFF)

    # Simply binary AND to obtain 8 bits representing blue
    b = (num & 0xFF)
    return (r, g, b)

Eu sei que existem maneiras mais eficientes de se conseguir isso, mas acredito que este é um exemplo realmente conciso que ilustra as operações booleanas de mudança e bit a bit.


14

Acho que a segunda parte da pergunta:

Além disso, para que os operadores bit a bit são realmente usados? Eu gostaria de alguns exemplos.

Foi abordado apenas parcialmente. Esses são meus dois centavos nesse assunto.

As operações bit a bit em linguagens de programação desempenham um papel fundamental ao lidar com muitos aplicativos. Quase toda a computação de baixo nível deve ser feita usando esse tipo de operação.

Em todos os aplicativos que precisam enviar dados entre dois nós, como:

  • redes de computadores;

  • aplicações de telecomunicações (telefones celulares, comunicações por satélite, etc).

Na camada de comunicação de nível inferior, os dados geralmente são enviados no que chamamos de quadros . Os frames são apenas strings de bytes enviados por meio de um canal físico. Esses quadros geralmente contêm os dados reais mais alguns outros campos (codificados em bytes) que fazem parte do que é chamado de cabeçalho . O cabeçalho geralmente contém bytes que codificam algumas informações relacionadas ao status da comunicação (por exemplo, com sinalizadores (bits)), contadores de quadros, códigos de correção e detecção de erros, etc. Para obter os dados transmitidos em um quadro e construir o quadros para enviar dados, você precisará de operações bit a bit certas.

Em geral, ao lidar com esse tipo de aplicativo, uma API está disponível para que você não precise lidar com todos esses detalhes. Por exemplo, todas as linguagens de programação modernas fornecem bibliotecas para conexões de soquete, portanto, você não precisa realmente construir os quadros de comunicação TCP / IP. Mas pense nas boas pessoas que programaram essas APIs para você, elas tiveram que lidar com a construção de quadros com certeza; usando todos os tipos de operações bit a bit para ir e voltar da comunicação de nível inferior para a de nível superior.

Como um exemplo concreto, imagine que alguém lhe dê um arquivo que contém dados brutos que foram capturados diretamente pelo hardware de telecomunicações. Neste caso, para encontrar os quadros, você precisará ler os bytes brutos do arquivo e tentar encontrar algum tipo de palavra de sincronização, escaneando os dados bit a bit. Depois de identificar as palavras de sincronização, você precisará obter os quadros reais e SHIFT- los se necessário (e isso é apenas o início da história) para obter os dados reais que estão sendo transmitidos.

Outra família de aplicativos de baixo nível muito diferente é quando você precisa controlar o hardware usando algumas portas (tipo antigas), como portas paralelas e seriais. Essas portas são controladas configurando alguns bytes, e cada bit desses bytes tem um significado específico, em termos de instruções, para aquela porta (ver por exemplo http://en.wikipedia.org/wiki/Parallel_port ). Se você quiser construir um software que faça algo com esse hardware, você precisará de operações bit a bit para traduzir as instruções que deseja executar para os bytes que a porta entende.

Por exemplo, se você tiver alguns botões físicos conectados à porta paralela para controlar algum outro dispositivo, esta é uma linha de código que você pode encontrar no aplicativo soft:

read = ((read ^ 0x80) >> 4) & 0x0f; 

Espero que isso contribua.


Eu adicionaria en.wikipedia.org/wiki/Bit_banging como outro caminho a ser explorado, especialmente se estiver lendo sobre portas paralelas e seriais como um exemplo em que operações bit a bit podem ser úteis.
Dan

6

Espero que isso esclareça esses dois:

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1

4
Opa ... tentei ser a arma mais rápida do oeste ... acabou como um idiota que nem sabe binário para dois :( Corrigido.
Amarghosh

1
x & 1não ilustra o efeito tão bem como o x & 2faria.
dansalmo

5

Pense em 0 como falso e 1 como verdadeiro. Então, bit a bit e (&) e ou (|) funcionam exatamente como regular e e ou, exceto que eles fazem todos os bits no valor de uma vez. Normalmente, você os verá usados ​​para sinalizadores se tiver 30 opções que podem ser definidas (digamos, como estilos de desenho em uma janela), você não deseja ter que passar 30 valores booleanos separados para definir ou remover cada um, então use | para combinar opções em um único valor e, em seguida, usar & para verificar se cada opção está definida. Este estilo de passagem de sinalizador é muito usado pelo OpenGL. Uma vez que cada bit é um sinalizador separado, você obtém valores de sinalizador em potências de dois (também conhecidos como números que têm apenas um conjunto de bits) 1 (2 ^ 0) 2 (2 ^ 1) 4 (2 ^ 2) 8 (2 ^ 3) o a potência de dois informa qual bit está definido se o sinalizador estiver ativado.

Observe também que 2 = 10, portanto, x | 2 é 110 (6) e não 111 (7) Se nenhum dos bits se sobrepuser (o que é verdadeiro neste caso) | atua como adição.


5

Não vi isso mencionado acima, mas você também verá que algumas pessoas usam deslocamento para a esquerda e para a direita para operações aritméticas. Um deslocamento para a esquerda por x é equivalente a multiplicar por 2 ^ x (desde que não transborde) e um deslocamento para a direita é equivalente a dividir por 2 ^ x.

Recentemente, vi pessoas usando x << 1 e x >> 1 para dobrar e dividir pela metade, embora não tenha certeza se elas estão apenas tentando ser inteligentes ou se realmente há uma vantagem distinta sobre os operadores normais.


1
Eu não sei sobre python, mas em linguagens de nível inferior como C ou ainda inferior - assembly, o deslocamento bit a bit é muito mais eficiente. Para ver a diferença, você pode escrever um programa em C fazendo isso de cada maneira e apenas compilar para o código assembly (ou se você conhece a linguagem assembly, já deve saber :)). Veja a diferença no número de instruções.
0xc0de

2
Meu argumento contra o uso dos operadores de mudança de bit seria que a maioria dos compiladores modernos provavelmente já está otimizando operações aritméticas, de modo que a inteligência é, na melhor das hipóteses, discutível ou, na pior, lutando contra o compilador. Não tenho experiência em C, compiladores ou designs de CPU e, portanto, não presumo que esteja correto. :)
P. Stallworth

Isso deve ser maior. Tive de lidar com alguns códigos que usavam o operador bit a bit exatamente dessa maneira, e essa resposta me ajudou a descobrir as coisas.
Philippe Oger

4

Jogos

Os conjuntos podem ser combinados usando operações matemáticas.

  • O operador de união |combina dois conjuntos para formar um novo contendo itens em ambos.
  • O operador de interseção &obtém itens apenas em ambos.
  • O operador de diferença -obtém itens no primeiro conjunto, mas não no segundo.
  • O operador de diferença simétrica ^obtém itens em qualquer um dos conjuntos, mas não em ambos.

Tente você mesmo:

first = {1, 2, 3, 4, 5, 6}
second = {4, 5, 6, 7, 8, 9}

print(first | second)

print(first & second)

print(first - second)

print(second - first)

print(first ^ second)

Resultado:

{1, 2, 3, 4, 5, 6, 7, 8, 9}

{4, 5, 6}

{1, 2, 3}

{8, 9, 7}

{1, 2, 3, 7, 8, 9}

Essa resposta não tem nenhuma relação com a pergunta e parece ter sido copiada e colada de outro lugar.
doctaphred de

A questão pergunta "Para que os operadores bit a bit realmente são usados?". Esta resposta fornece um uso menos conhecido, mas muito útil dos operadores bit a bit.
Taegyung

3

Este exemplo mostrará as operações para todos os quatro valores de 2 bits:

10 | 12

1010 #decimal 10
1100 #decimal 12

1110 #result = 14

10 & 12

1010 #decimal 10
1100 #decimal 12

1000 #result = 8

Aqui está um exemplo de uso:

x = raw_input('Enter a number:')
print 'x is %s.' % ('even', 'odd')[x&1]

2

Outro caso de uso comum é a manipulação / teste de permissões de arquivo. Consulte o módulo de estatísticas do Python: http://docs.python.org/library/stat.html .

Por exemplo, para comparar as permissões de um arquivo com um conjunto de permissões desejado, você pode fazer algo como:

import os
import stat

#Get the actual mode of a file
mode = os.stat('file.txt').st_mode

#File should be a regular file, readable and writable by its owner
#Each permission value has a single 'on' bit.  Use bitwise or to combine 
#them.
desired_mode = stat.S_IFREG|stat.S_IRUSR|stat.S_IWUSR

#check for exact match:
mode == desired_mode
#check for at least one bit matching:
bool(mode & desired_mode)
#check for at least one bit 'on' in one, and not in the other:
bool(mode ^ desired_mode)
#check that all bits from desired_mode are set in mode, but I don't care about 
# other bits.
not bool((mode^desired_mode)&desired_mode)

Eu elenco os resultados como booleanos, porque eu só me importo com a verdade ou a falsidade, mas seria um exercício válido imprimir os valores bin () para cada um.


1
Você está errado no último exemplo. Aqui como ele deve se parece com: not bool((mode ^ desired_mode) & 0777). Ou (mais fácil de entender): not (mode & 0777) ^ desired_mode == 0. E deixará apenas os bits interessantes, o XOR verificará que todos os bits desejados estão configurados. A == 0comparação explícita é mais significativa do que bool().
Vadim Fint

Não acho que isso seja específico para operações de arquivo. Por exemplo, no PyQt você faz algo semelhante para setWindowFlags. Exemplo: setWindowFlags(SplashScreen | WindowStaysOnTopHint). Ainda acho isso confuso, pois parece que você está definindo um botão de alternância para 'ligado', então parece mais intuitivo 'e' nesse caso.
Eric

2

As representações de bits de inteiros são freqüentemente usadas na computação científica para representar matrizes de informações verdadeiro-falsas porque uma operação bit a bit é muito mais rápida do que iterar por meio de uma matriz de booleanos. (Linguagens de nível superior podem usar a ideia de uma matriz de bits.)

Um bom e simples exemplo disso é a solução geral para o jogo Nim. Dê uma olhada no código Python na página da Wikipedia . Faz uso intenso de bit a bit exclusivo ou ^,.


1

Pode haver uma maneira melhor de descobrir onde um elemento da matriz está entre dois valores, mas, como mostra este exemplo, o & funciona aqui, enquanto e não funciona.

import numpy as np
a=np.array([1.2, 2.3, 3.4])
np.where((a>2) and (a<3))      
#Result: Value Error
np.where((a>2) & (a<3))
#Result: (array([1]),)

1

Não vi isso ser mencionado. Este exemplo mostrará a operação decimal (-) para valores de 2 bits: AB (somente se A contiver B)

esta operação é necessária quando mantemos um verbo em nosso programa que representa bits. às vezes precisamos adicionar bits (como acima) e às vezes precisamos remover bits (se o verbo contiver então)

111 #decimal 7
-
100 #decimal 4
--------------
011 #decimal 3

com python: 7 & ~ 4 = 3 (remova de 7 os bits que representam 4)

001 #decimal 1
-
100 #decimal 4
--------------
001 #decimal 1

com python: 1 & ~ 4 = 1 (remova de 1 os bits que representam 4 - neste caso, 1 não é 'contém' 4) ..


0

Embora a manipulação de bits de um inteiro seja útil, muitas vezes para protocolos de rede, que podem ser especificados até o bit, pode-se exigir a manipulação de sequências de bytes mais longas (que não são facilmente convertidas em um inteiro). Neste caso, é útil empregar a biblioteca bitstring que permite operações bit a bit nos dados - por exemplo, pode-se importar a string 'ABCDEFGHIJKLMNOPQ' como uma string ou como hex e bit shift-lo (ou realizar outras operações bit a bit):

>>> import bitstring
>>> bitstring.BitArray(bytes='ABCDEFGHIJKLMNOPQ') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')
>>> bitstring.BitArray(hex='0x4142434445464748494a4b4c4d4e4f5051') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')

0

os seguintes operadores bit a bit: & , | , ^ e ~ retornam valores (com base em suas entradas) da mesma forma que as portas lógicas afetam os sinais. Você pode usá-los para emular circuitos.


0

Para inverter os bits (ou seja, complemento / inversão de 1), você pode fazer o seguinte:

Como o valor ExORed com todos os resultados 1s em inversão, para uma determinada largura de bit, você pode usar ExOR para invertê-los.

In Binary
a=1010 --> this is 0xA or decimal 10
then 
c = 1111 ^ a = 0101 --> this is 0xF or decimal 15
-----------------
In Python
a=10
b=15
c = a ^ b --> 0101
print(bin(c)) # gives '0b101'
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.