¿Qué son los operadores bit a bit?

129

Soy alguien que escribe código solo por diversión y realmente no he profundizado en él, ya sea en un entorno académico o profesional, así que cosas como estos operadores bit a bit realmente se me escapan.

Estaba leyendo un artículo sobre JavaScript, que aparentemente admite operaciones bit a bit. Sigo viendo esta operación mencionada en algunos lugares, y he intentado leer para averiguar qué es exactamente, pero parece que no lo entiendo en absoluto. Entonces, ¿qué son? ¡Ejemplos claros serían geniales! :RE

Solo unas pocas preguntas más: ¿cuáles son algunas aplicaciones prácticas de las operaciones bit a bit? ¿Cuándo podrías usarlos?

hacer clic
fuente
2
Para más preguntas, es posible que desee agregar una nueva pregunta SO y hacer referencia a esta. Probablemente obtendrá un mejor conjunto de respuestas de esa manera.
Greg Hewgill

Respuestas:

186

Como nadie ha abordado el tema de por qué son útiles:

Utilizo mucho las operaciones bit a bit cuando trabajo con banderas. Por ejemplo, si desea pasar una serie de banderas a una operación (por ejemplo,File.Open() , con el modo de lectura y el modo de escritura habilitados), puede pasarlos como un solo valor. Esto se logra asignando a cada indicador posible su propio bit en un conjunto de bits (byte, short, int o long). Por ejemplo:

 Read: 00000001
Write: 00000010

Entonces, si desea pasar de lectura Y escritura, pasaría (LEER | ESCRIBIR) que luego combina los dos en

00000011

Que luego se puede descifrar en el otro extremo como:

if ((flag & Read) != 0) { //...

que verifica

00000011 &
00000001

que vuelve

00000001

que no es 0, por lo que el indicador especifica READ.

Puede usar XOR para alternar varios bits. Lo he usado cuando uso una bandera para especificar entradas direccionales (Arriba, Abajo, Izquierda, Derecha). Por ejemplo, si un sprite se mueve horizontalmente y quiero que se dé la vuelta:

     Up: 00000001
   Down: 00000010
   Left: 00000100
  Right: 00001000
Current: 00000100

Simplemente XOR el valor actual con (IZQUIERDA | DERECHA) que apagará IZQUIERDA y DERECHA, en este caso.

Bit Shifting es útil en varios casos.

x << y

es lo mismo que

x * 2 y

si necesita multiplicar rápidamente por una potencia de dos, pero tenga cuidado con el cambio de 1 bit al bit superior; esto hace que el número sea negativo a menos que no esté firmado. También es útil cuando se trata con diferentes tamaños de datos. Por ejemplo, leer un número entero de cuatro bytes:

int val = (A << 24) | (B << 16) | (C << 8) | D;

Suponiendo que A es el byte más significativo y D el menor. Terminaría como:

A = 01000000
B = 00000101
C = 00101011
D = 11100011
val = 01000000 00000101 00101011 11100011

Los colores a menudo se almacenan de esta manera (con el byte más significativo ignorado o utilizado como Alfa):

A = 255 = 11111111
R = 21 = 00010101
G = 255 = 11111111
B = 0 = 00000000
Color = 11111111 00010101 11111111 00000000

Para encontrar los valores nuevamente, simplemente mueva los bits hacia la derecha hasta que esté en la parte inferior, luego oculte los bits restantes de orden superior:

Int Alpha = Color >> 24
Int Red = Color >> 16 & 0xFF
Int Green = Color >> 8 & 0xFF
Int Blue = Color & 0xFF

0xFFes el mismo que 11111111. Entonces, esencialmente, para Red, estarías haciendo esto:

Color >> 16 = (filled in 00000000 00000000)11111111 00010101  (removed 11111111 00000000)
00000000 00000000 11111111 00010101 &
00000000 00000000 00000000 11111111 =
00000000 00000000 00000000 00010101 (The original value)
Ed Marty
fuente
x << n, entonces n debe tener la forma de 2 ^ valor?
Ahmed C
28

Vale la pena señalar que las tablas de verdad de un solo bit enumeradas como otras respuestas funcionan en solo uno o dos bits de entrada a la vez. Qué sucede cuando usas enteros, como:

int x = 5 & 6;

La respuesta está en la expansión binaria de cada entrada:

  5 = 0 0 0 0 0 1 0 1
& 6 = 0 0 0 0 0 1 1 0
---------------------
      0 0 0 0 0 1 0 0

Cada par de bits en cada columna se ejecuta a través de la función "Y" para dar el bit de salida correspondiente en la línea inferior. Entonces, la respuesta a la expresión anterior es 4. La CPU ha realizado (en este ejemplo) 8 operaciones "Y" separadas en paralelo, una para cada columna.

Menciono esto porque todavía recuerdo haber tenido este "¡AHA!" momento en que me enteré de esto hace muchos años.

Greg Hewgill
fuente
Wow, eso tiene mucho más sentido ahora. Parecía mucho más complicado de lo que aparentemente es. Gracias. No estoy seguro de cuál elegir como la respuesta correcta, ya que hay muchas buenas, y no puedo votar, así que ... gracias
Haga clic en
27

Los operadores bit a bit son operadores que trabajan en un bit a la vez.

AND es 1 solo si sus dos entradas son 1.

OR es 1 si una o más de sus entradas son 1.

XOR es 1 solo si exactamente una de sus entradas es 1.

NOT es 1 solo si su entrada es 0.

Estos se pueden describir 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 valores (dos en el caso de NOT ya que solo tiene una entrada) que se muestran en la intersección de las dos entradas.

AND|0 1      OR|0 1
---+----    ---+----
  0|0 0       0|0 1
  1|0 1       1|1 1

XOR|0 1     NOT|0 1
---+----    ---+---
  0|0 1        |1 0
  1|1 0

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

    203: 1100 1011
AND  15: 0000 1111
------------------
 IS  11: 0000 1011
paxdiablo
fuente
16

Estos son los operadores bit a bit, todos compatibles con JavaScript:

  • op1 & op2- El ANDoperador compara dos bits y genera un resultado de 1 si ambos bits son 1; de lo contrario, devuelve 0.

  • op1 | op2- El ORoperador compara dos bits y genera un resultado de 1 si los bits son complementarios; de lo contrario, devuelve 0.

  • op1 ^ op2- El EXCLUSIVE-ORoperador compara dos bits y devuelve 1 si alguno de los bits es 1 y da 0 si ambos bits son 0 o 1.

  • ~op1- El COMPLEMENToperador se utiliza para invertir todos los bits del operando.

  • op1 << op2- El SHIFT LEFToperador mueve los bits hacia la izquierda, descarta el bit más a la izquierda y asigna al bit más a la derecha un valor de 0. Cada movimiento hacia la izquierda multiplica efectivamente op1 por 2.

  • op1 >> op2- El SHIFT RIGHToperador mueve los bits hacia la derecha, descarta el bit más a la derecha y asigna al bit más a la izquierda un valor de 0. Cada movimiento hacia la derecha divide efectivamente op1 por la mitad. El bit de signo más a la izquierda se conserva.

  • op1 >>> op2- El operador SHIFT RIGHT- ZERO FILLmueve los bits hacia la derecha, descarta el bit más a la derecha y asigna al bit más a la izquierda un valor de 0. Cada movimiento hacia la derecha divide efectivamente op1 por la mitad. El bit de signo más a la izquierda se descarta.

Jeff Hillman
fuente
"si los bits son complementarios" - ¿qué?
Andrey Tyukin
@AndreyTyukin dos bits son complementarios si uno de ellos es 1 y el otro es 0.
Jeff Hillman
@JeffHillman Según su descripción en el comentario, 1 y 1 no son "complementarios". Entonces no me queda claro por qué 1 | 1da 1y no 0, y cómo| se supone que es diferente ^. Tuve que usar este Q / A como un objetivo duplicado hace unos días, y deseé que después de 10 años uno tuviera un duplicado canónico más claro para este tipo de preguntas.
Andrey Tyukin
4

Para desglosarlo un poco más, tiene mucho que ver con la representación binaria del valor en cuestión.

Por ejemplo (en decimal):
x = 8
y = 1

saldría a (en binario):
x = 1000
y = 0001

A partir de ahí, puede realizar operaciones computacionales como 'y' o 'o'; en este caso:
x | y =
1000 
0001 |
------
1001

o ... 9 en decimal

Espero que esto ayude.

javamonkey79
fuente
|es un quirófano?
Si8
Por alguna razón, esto tenía más sentido para mí. Sin embargo, todavía no estoy seguro de la x | y = 1000 0001 |parte
samayo
4

Cuando se menciona el término "bit a bit", a veces aclara que no es un operador "lógico".

Por ejemplo, en JavaScript, los operadores bit a bit tratan sus operandos como una secuencia de 32 bits (ceros y unos) ; mientras tanto, los operadores lógicos generalmente se usan con valores booleanos (lógicos) pero pueden funcionar con tipos no booleanos.

Tome expr1 && expr2 por ejemplo.

Devuelve expr1 si se puede convertir a falso; de lo contrario, devuelve expr2. Por lo tanto, cuando se usa con valores booleanos, && devuelve verdadero si ambos operandos son verdaderos; de lo contrario, devuelve falso.

a = "Cat" && "Dog"     // t && t returns Dog
a = 2 && 4     // t && t returns 4

Como otros han señalado, 2 y 4 es un AND bit a bit, por lo que devolverá 0.

Puede copiar lo siguiente a test.html o algo y probar:

<html>
<body>
<script>
    alert("\"Cat\" && \"Dog\" = " + ("Cat" && "Dog") + "\n"
        + "2 && 4 = " + (2 && 4) + "\n"
        + "2 & 4 = " + (2 & 4));
</script>
Eugene Yokota
fuente
3

En la programación de computadoras digitales, una operación bit a bit opera en uno o más patrones de bits o números binarios al nivel de sus bits individuales. Es una acción rápida y primitiva directamente compatible con el procesador, y se utiliza para manipular valores para comparaciones y cálculos.

operaciones :

  • bitwise Y

  • bitwise O

  • NO bit a bit

  • bitor XOR

  • etc.

Elemento de la lista

    AND|0 1        OR|0 1 
    ---+----      ---+---- 
      0|0 0         0|0 1 
      1|0 1         1|1 1 

   XOR|0 1        NOT|0 1 
   ---+----       ---+--- 
     0|0 1           |1 0 
     1|1 0

P.ej.

    203: 1100 1011
AND  15: 0000 1111
------------------
  =  11: 0000 1011

Usos del operador bit a bit

  • Los operadores de desplazamiento a la izquierda y desplazamiento a la derecha son equivalentes a la multiplicación y división por x * 2 y respectivamente.

P.ej.

int main()
{
     int x = 19;
     printf ("x << 1 = %d\n" , x <<1);
     printf ("x >> 1 = %d\n", x >>1);
     return 0;
}
// Output: 38 9
  • El operador & puede usarse para verificar rápidamente si un número es par o impar

P.ej.

int main()
{
    int x = 19;
    (x & 1)? printf("Odd"): printf("Even");
    return 0;
 }
// Output: Odd
  • Búsqueda rápida mínima de x e y sin if elsedeclaración

P.ej.

int min(int x, int y)
{
    return y ^ ((x ^ y) & - (x < y))
}
  • Conversión decimal a binaria

P.ej.

#include <stdio.h>
int main ()
{
    int n , c , k ;
    printf("Enter an integer in decimal number system\n " ) ;
    scanf( "%d" , & n );
    printf("%d in binary number
    system is: \n " , n ) ;
    for ( c = 31; c >= 0 ; c -- )
    {
         k = n >> c ;
         if ( k & 1 )
              printf("1" ) ;
         else
              printf("0" ) ;
      }
      printf(" \n " );
      return 0 ;
}
  • El cifrado de compuerta XOR es una técnica popular, debido a su complejidad y uso posterior por parte del programador.
  • El operador XOR bitwise es el operador más útil desde la perspectiva de la entrevista técnica.

el desplazamiento a nivel de bits solo funciona con + ve número

También hay una amplia gama de uso de lógica bit a bit

Prasante
fuente
"complixblity and reare ..."?
Jonathan Cross
The left-shift and right-shift operators are equivalent to multiplication and division by x * 2y respectively.¡Así es! muyiy.cn/question/program/102.html
xgqfrms
mi solución repl.it/@xgqfrms/…
xgqfrms
1

Puede ser útil pensar de esta manera. Así es como funciona AND (&):

Básicamente dice que son ambos números, así que si tienes dos números 5 y 3 se convertirán en binarios y la computadora pensará

         5: 00000101
         3: 00000011

ambos son uno: 00000001 0 es falso, 1 es verdadero

Entonces el AND de 5 y 3 es uno. El operador OR (|) hace lo mismo, excepto que solo uno de los números debe ser uno para la salida 1, no ambos.

usuario3677963
fuente
-5

Seguía escuchando lo lentos que eran los operadores de JavaScript a nivel de bits. Hice algunas pruebas para mi última publicación de blog y descubrí que eran 40% a 80% más rápidas que la alternativa aritmética en varias pruebas. Quizás solían ser lentos. En los navegadores modernos, los amo.

Tengo un caso en mi código que será más rápido y fácil de leer debido a esto. Mantendré mis ojos abiertos por más.

Nosredna
fuente