Operación y uso bit a bit


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

Puedo entender los operadores aritméticos en Python (y otros lenguajes), pero nunca entendí bien los operadores 'bit a bit'. En el ejemplo anterior (de un libro de Python), entiendo el desplazamiento a la izquierda pero no los otros dos.

Además, ¿para qué se utilizan realmente los operadores bit a bit? Agradecería algunos ejemplos.



Respuestas:


163

Los operadores bit a bit son operadores que trabajan con valores de varios bits, pero conceptualmente un bit a la vez.

  • ANDes 1 solo si ambas entradas son 1; de lo contrario, es 0.
  • ORes 1 si una o ambas entradas son 1; de lo contrario, es 0.
  • XORes 1 solo si exactamente una de sus entradas es 1, de lo contrario es 0.
  • NOT es 1 solo si su entrada es 0, de lo contrario es 0.

Suelen mostrarse mejor como tablas de verdad. Las posibilidades de entrada están en la parte superior e izquierda, el bit resultante es uno de los cuatro (dos en el caso de NOT, ya que solo tiene una entrada) valores que se muestran en la intersección de las 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

Un ejemplo es si solo desea los 4 bits inferiores de un entero, lo Y con 15 (binario 1111), así:

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

En ese caso, los bits cero en 15 actúan efectivamente como un filtro, lo que obliga a que los bits del resultado también sean cero.

Además, >>y<< menudo se incluyen como operadores bit a bit, y "desplazan" un valor respectivamente a la derecha y a la izquierda en un cierto número de bits, desechando los bits que se mueven del final hacia el que se está desplazando y alimentando bits cero en el Otro final.

Así por ejemplo:

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

Tenga en cuenta que el desplazamiento a la izquierda en Python es inusual porque no usa un ancho fijo donde se descartan los bits; mientras que muchos lenguajes usan un ancho fijo según el tipo de datos, Python simplemente expande el ancho para atender bits adicionales. Para obtener el comportamiento de descarte en Python, puede seguir un desplazamiento a la izquierda con un bit a bit and, como en un valor de 8 bits desplazando cuatro bits a la izquierda:

bits8 = (bits8 << 4) & 255

Con eso en mente, otro ejemplo de operadores bit a bit es si tiene dos valores de 4 bits que desea empaquetar en uno de 8 bits, puede usar los tres de sus operadores ( left-shift, andy or):

packed_val = ((val1 & 15) << 4) | (val2 & 15)
  • La & 15operación se asegurará de que ambos valores solo tengan los 4 bits inferiores.
  • El << 4es un desplazamiento de 4 bits a la izquierda para moverseval1 en la parte superior 4 bits de un valor de 8 bits.
  • El |simplemente combina estos dos juntos.

Si val1es 7 y val2es 4:

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

43

Un uso típico:

| se usa para establecer un cierto bit en 1

& se usa para probar o borrar un cierto bit

  • Establezca un bit (donde n es el número de bit y 0 es el bit menos significativo):

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

  • Borrar un poco:

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

  • Alternar un poco:

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

  • Prueba un poco:

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

Tome el caso de su lista, por ejemplo:

x | 2se utiliza para establecer el bit 1 de xen 1

x & 1se utiliza para probar si el bit 0 de xes 1 o 0


38

¿Para qué se utilizan realmente los operadores bit a bit? Agradecería algunos ejemplos.

Uno de los usos más comunes de las operaciones bit a bit es para analizar colores hexadecimales.

Por ejemplo, aquí hay una función de Python que acepta una cadena como #FF09BEy devuelve una tupla de sus valores rojo, verde y 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)

Sé que hay formas más eficientes de lograr esto, pero creo que este es un ejemplo realmente conciso que ilustra tanto los cambios como las operaciones booleanas bit a bit.


14

Creo que la segunda parte de la pregunta:

Además, ¿para qué se utilizan realmente los operadores bit a bit? Agradecería algunos ejemplos.

Solo se ha abordado parcialmente. Estos son mis dos centavos al respecto.

Las operaciones bit a bit en lenguajes de programación juegan un papel fundamental cuando se trata de una gran cantidad de aplicaciones. Casi toda la computación de bajo nivel debe realizarse utilizando este tipo de operaciones.

En todas las aplicaciones que necesitan enviar datos entre dos nodos, como:

  • Red de computadoras;

  • aplicaciones de telecomunicaciones (teléfonos móviles, comunicaciones por satélite, etc.).

En la capa de comunicación de nivel inferior, los datos generalmente se envían en lo que se denomina tramas . Las tramas son solo cadenas de bytes que se envían a través de un canal físico. Estos marcos generalmente contienen los datos reales más algunos otros campos (codificados en bytes) que son parte de lo que se llama encabezado . El encabezado generalmente contiene bytes que codifican alguna información relacionada con el estado de la comunicación (por ejemplo, con banderas (bits)), contadores de tramas, códigos de corrección y detección de errores, etc. Para obtener los datos transmitidos en una trama y construir marcos para enviar datos, necesitará operaciones seguras a nivel de bits.

En general, cuando se trata de ese tipo de aplicaciones, hay una API disponible para que no tenga que ocuparse de todos esos detalles. Por ejemplo, todos los lenguajes de programación modernos proporcionan bibliotecas para conexiones de socket, por lo que en realidad no necesita construir los marcos de comunicación TCP / IP. Pero piense en las buenas personas que programaron esas API para usted, seguro que tuvieron que lidiar con la construcción de marcos; utilizando todo tipo de operaciones bit a bit para ir y venir de la comunicación de bajo nivel a la de nivel superior.

Como ejemplo concreto, imagine que alguien le da un archivo que contiene datos sin procesar que fueron capturados directamente por hardware de telecomunicaciones. En este caso, para encontrar los fotogramas, deberá leer los bytes sin procesar en el archivo e intentar encontrar algún tipo de palabras de sincronización, escaneando los datos bit a bit. Después de identificar las palabras de sincronización, necesitará obtener los fotogramas reales y CAMBIARlos si es necesario (y ese es solo el comienzo de la historia) para obtener los datos reales que se están transmitiendo.

Otra familia de aplicaciones de bajo nivel muy diferente es cuando necesita controlar el hardware usando algún tipo de puertos (antiguos), como puertos paralelos y seriales. Estos puertos se controlan estableciendo algunos bytes, y cada bit de esos bytes tiene un significado específico, en términos de instrucciones, para ese puerto (consulte, por ejemplo, http://en.wikipedia.org/wiki/Parallel_port ). Si desea crear software que haga algo con ese hardware, necesitará operaciones bit a bit para traducir las instrucciones que desea ejecutar a los bytes que el puerto comprende.

Por ejemplo, si tiene algunos botones físicos conectados al puerto paralelo para controlar algún otro dispositivo, esta es una línea de código que puede encontrar en la aplicación software:

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

Espero que esto contribuya.


Agregaría en.wikipedia.org/wiki/Bit_banging como otra vía para explorar, especialmente si lee sobre puertos paralelos y seriales como un ejemplo donde las operaciones bit a bit pueden ser útiles.
Dan

6

Espero que esto aclare esos dos:

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1

4
Vaya ... traté de ser el arma más rápida del oeste ... terminó como un idiota que ni siquiera sabe binario para dos :( Lo arregló.
Amarghosh

1
x & 1no ilustra el efecto tan bien como lo x & 2haría.
dansalmo

5

Piense en 0 como falso y 1 como verdadero. Luego, bit a bit y (&) yo (|) funcionan igual que los normales yo, excepto que hacen todos los bits del valor a la vez. Por lo general, verá que se usan para banderas si tiene 30 opciones que se pueden configurar (por ejemplo, como estilos de dibujo en una ventana) y no quiere tener que pasar 30 valores booleanos separados para configurar o desarmar cada uno, por lo que usa | para combinar opciones en un solo valor y luego use & para verificar si cada opción está configurada. Este estilo de paso de bandera es muy utilizado por OpenGL. Como cada bit es una bandera separada, obtienes valores de bandera en potencias de dos (también conocidos como números que tienen solo un bit establecido) 1 (2 ^ 0) 2 (2 ^ 1) 4 (2 ^ 2) 8 (2 ^ 3) el el poder de dos le dice qué bit se establece si la bandera está encendida.

También tenga en cuenta 2 = 10, por lo que x | 2 es 110 (6) no 111 (7) Si ninguno de los bits se superpone (lo cual es cierto en este caso) | actúa como una adición.


5

No lo vi mencionado anteriormente, pero también verá que algunas personas usan el desplazamiento a la izquierda y a la derecha para operaciones aritméticas. Un desplazamiento a la izquierda por x equivale a multiplicar por 2 ^ x (siempre que no se desborde) y un desplazamiento a la derecha equivale a dividir por 2 ^ x.

Recientemente, he visto personas que usan x << 1 yx >> 1 para duplicar y reducir a la mitad, aunque no estoy seguro de si solo están tratando de ser inteligentes o si realmente hay una ventaja clara sobre los operadores normales.


1
No sé sobre Python, pero en lenguajes de nivel inferior como C o incluso inferior, el ensamblaje, el desplazamiento bit a bit es mucho más eficiente. Para ver la diferencia, puede escribir un programa en C haciendo esto en cada forma y simplemente compilar en código ensamblador (o si conoce el lenguaje ensamblador, ya lo sabría :)). Vea la diferencia en el número de instrucciones.
0xc0de

2
Mi argumento en contra del uso de los operadores de desplazamiento de bits sería que la mayoría de los compiladores modernos probablemente ya estén optimizando las operaciones aritméticas, por lo que la inteligencia es, en el mejor de los casos, discutible o, en el peor, luchando contra el compilador. No tengo experiencia en C, compiladores o diseños de CPU, por lo que no supongo que esté en lo cierto. :)
P. Stallworth

Esto debería ser más alto. Tuve que lidiar con un código que usaba el operador bit a bit exactamente de esa manera, y esa respuesta me ayudó a resolver las cosas.
Philippe Oger

4

Conjuntos

Los conjuntos se pueden combinar mediante operaciones matemáticas.

  • El operador de unión |combina dos conjuntos para formar uno nuevo que contiene elementos en cualquiera.
  • El operador de intersección &obtiene elementos solo en ambos.
  • El operador de diferencia -obtiene elementos en el primer conjunto pero no en el segundo.
  • El operador de diferencia simétrica ^obtiene elementos en cualquiera de los conjuntos, pero no en ambos.

Inténtalo tú mismo:

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}

Esta respuesta no tiene nada que ver con la pregunta y parece que se copió y pegó desde otro lugar.
doctaphred

La pregunta es "¿Para qué se utilizan realmente los operadores bit a bit?". Esta respuesta proporciona un uso menos conocido pero muy útil de los operadores bit a bit.
Taegyung

3

Este ejemplo le mostrará las operaciones para los cuatro 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

Aquí hay un ejemplo de uso:

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

2

Otro caso de uso común es manipular / probar permisos de archivos. Consulte el módulo de estadísticas de Python: http://docs.python.org/library/stat.html .

Por ejemplo, para comparar los permisos de un archivo con un conjunto de permisos deseado, puede hacer 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)

Lanzo los resultados como booleanos, porque solo me importa la verdad o la falsedad, pero sería un ejercicio valioso imprimir los valores bin () para cada uno.


1
Estás equivocado en el último ejemplo. Aquí cómo debe el siguiente aspecto: not bool((mode ^ desired_mode) & 0777). O (más fácil de entender): not (mode & 0777) ^ desired_mode == 0. Y dejará solo bits interesantes, XOR comprobará qué bits están configurados. La == 0comparación explícita es más significativa que bool().
Vadim Fint

No creo que esto sea específico de las operaciones de archivos. Por ejemplo, en PyQt haces algo similar para setWindowFlags. Ejemplo: setWindowFlags(SplashScreen | WindowStaysOnTopHint). Todavía lo encuentro confuso, ya que parece un interruptor que está configurando en 'activado', por lo que parece más intuitivo para 'y' en tal caso.
Eric

2

Las representaciones de bits de enteros se utilizan a menudo en la informática científica para representar matrices de información verdadero-falso porque una operación bit a bit es mucho más rápida que iterar a través de una matriz de valores booleanos. (Los lenguajes de nivel superior pueden usar la idea de una matriz de bits).

Un ejemplo bonito y bastante simple de esto es la solución general al juego de Nim. Eche un vistazo al código Python en la página de Wikipedia . Se hace un uso intensivo de exclusiva bit a bit o, ^.


1

Puede haber una mejor manera de encontrar dónde está un elemento de matriz entre dos valores, pero como muestra este ejemplo, el & funciona aquí, mientras que y no.

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

No vi que se mencionara. Este ejemplo le mostrará la operación decimal (-) para valores de 2 bits: AB (solo si A contiene B)

esta operación es necesaria cuando tenemos un verbo en nuestro programa que representa bits. a veces necesitamos agregar bits (como arriba) y a veces necesitamos eliminar bits (si el verbo contiene entonces)

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

con python: 7 & ~ 4 = 3 (elimine de 7 los bits que representan 4)

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

con python: 1 & ~ 4 = 1 (elimine de 1 los bits que representan 4 - en este caso 1 no es 'contiene' 4) ..


0

Si bien la manipulación de bits de un número entero es útil, a menudo para protocolos de red, que se pueden especificar hasta el bit, se puede requerir la manipulación de secuencias de bytes más largas (que no se convierten fácilmente en un número entero). En este caso, es útil emplear la biblioteca de cadenas de bits que permite operaciones bit a bit en datos; por ejemplo, se puede importar la cadena 'ABCDEFGHIJKLMNOPQ' como una cadena o como hexadecimal y bit shift (o realizar otras operaciones bit a bit):

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

0

los siguientes operadores bit a bit: & , | , ^ y ~ devuelven valores (basados ​​en su entrada) de la misma manera que las puertas lógicas afectan las señales. Podrías usarlos para emular circuitos.


0

Para invertir bits (es decir, complemento / inversión de 1) puede hacer lo siguiente:

Dado que el valor ExORed con todos los 1 resulta en inversión, para un ancho de bit dado puede usar ExOR para invertirlos.

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'
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.