Los operadores bit a bit (bitwise) son símbolos que permiten trabajar de forma directa con la representación binaria de los números enteros. En lugar de operar sobre valores numéricos, evalúan y manipulan cada bit de sus operandos, de forma individual. Esto es esencial en operaciones de bajo nivel.
Estos operadores trabajan con operandos de 32 bits. Puesto que JavaScript guarda el tipo de dato number
como números de punto flotante de 64 bits, los convierte de forma automática. Antes de una operación bit a bit, el valor se convierte a un entero con signo de 32 bits. Después de la operación, el resultado se convierte de nuevo al formato de punto flotante de 64 bits.
En JavaScript hay siete operadores de este tipo:
Operador | Nombre | Descripción |
---|---|---|
& | AND bit a bit | Compara cada bit de los operandos y devuelve 1 si ambos son 1. |
| | OR bit a bit | Compara cada bit de los operandos y devuelve 1 si uno de los dos es 1. |
^ | XOR bit a bit | Devuelve 1 si los bits son distintos, y 0 si son iguales. |
~ | NOT bit a bit | Operador unario: invierte cada bit del operando (complemento a uno). |
<< | Desplazamiento a la izquierda | Desplaza los bits del operando izquierdo a la izquierda tantas posiciones como indique el operando derecho. Los espacios vacíos se rellenan con ceros. Equivale a multiplicar por potencias de 2. |
>> | Desplazamiento a la derecha con signo | Desplaza los bits del operando izquierdo a la derecha. Los bits de la izquierda se rellenan con el bit de signo (se conserva el signo del número). |
>>> | Desplazamiento a la derecha con ceros | Similar a >> , pero siempre rellena con ceros por la izquierda, sin conservar el signo. |
El operador AND bit a bit
El operador AND bit a bit se representa con un solo símbolo «ampersand» (&
). Este operador binario realiza la operación lógica AND comparando, de forma individual, cada bit de sus dos operandos enteros.
Su funcionamiento es simple: si los dos bits comparados son 1, el bit resultante será 1. En cualquier otro caso, el bit resultante será 0.
Por ejemplo, comparemos 9 & 5;
. En sistema binario, el 9 es 1001 y el 5 es 0101, sin olvidar que JavaScript trabaja con 32 bits en vez de 4. Por cada par de bits obtenemos un bit resultante:
Bit 1 | Bit 2 | Resultado (con & ) |
---|---|---|
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
1 | 1 | 1 |
En este ejemplo, el único bit que resulta en 1 es el de la posición más a la derecha, porque ambos operandos tenían 1. El valor resultante es 0001, que en el sistema decimal equivale a 1. Por lo tanto: 9 & 5;
es 1.
El operador OR bit a bit
El operador OR bit a bit se representa con una sola barra vertical (|
). Se trata de un operador binario que realiza la operación lógica OR comparando los bits de forma individual de sus dos operandos enteros.
Su funcionamiento interno es el siguiente: si uno de los dos bits es 1, el bit resultante será 1. Si ninguno de los bits es 1, el bit resultante será 0.
Ahora comparemos 9 | 5;
. En sistema binario, tomando solo los últimos 4 bits en lugar de los 32 que utiliza JavaScript, el 9 es 1001 y el 5 es 0101. Tras comparar cada par de bits obtenemos un bit resultante:
Bit 1 | Bit 2 | Resultado (con | ) |
---|---|---|
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
1 | 1 | 1 |
En este ejemplo, el único bit que resulta en 0 es el tercero. Esto sucede porque ambos operandos tenían 0. El valor resultante es 1101, que traducido al sistema decimal es 13. Por tanto: 9 | 5;
es 13.
El operador XOR bit a bit
El operador XOR bit a bit (o «OR exclusivo») se identifica con el símbolo del acento circunflejo (^
). Es un operador binario que ejecuta la operación lógica XOR al confrontar de manera individual cada bit de sus dos operandos enteros.
La lógica interna de esta operación se basa en la diferencia: el bit resultante será 1 solo si los dos bits comparados son distintos. Por tanto, si ambos bits son iguales (1 y 1, o 0 y 0), el bit resultante será 0.
Retomemos la comparación de los números 9 y 5: 9 ^ 5;
. En la tabla siguiente se muestra la confrontación de cada par de bits y el valor obtenido:
Bit 1 | Bit 2 | Resultado (con ^ ) |
---|---|---|
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
1 | 1 | 0 |
En este ejemplo, el valor binario resultante es 1100, que traducido al sistema decimal es el 12. Este resultado se debe a que los dos primeros pares de operandos eran diferentes, resultando en 1, mientras que los dos últimos pares son iguales, resultando en 0. Por lo tanto: 9 ^ 5;
equivale a 12.
El operador NOT bit a bit
El operador NOT bit a bit (o «complemento a uno») se representa con la virguilla (~
). A diferencia de los operadores anteriores, este es un operador es unario, ya que se aplica a un único operando entero. Su función es invertir el valor de cada bit del número: donde hay un 1, coloca un 0, y donde hay un 0, coloca un 1.
En otras palabras, el operador ~
niega todos los bits del número con el que trabaja. Además, es importante recordar que JavaScript utiliza una representación de 32 bits con signo para los números enteros en las operaciones bit a bit. De forma resumida: ~x
equivale a la fórmula -(x + 1)
.
Vamos a ver un ejemplo con el número 5 (…00000101 en 32 bits). En la tabla siguiente solo mostramos la inversión en los últimos 4 bits:
Bit original | Inversión (con ~ ) |
---|---|
0 | 1 |
1 | 0 |
0 | 1 |
1 | 0 |
¡Ojo! Esto no significa que el resultado sea 10. El resultado de esta inversión (~5;
) es la representación binaria de -6 en 32 bits. Para simplificar el proceso, solo necesitamos recordar la fórmula que JavaScript aplica internamente. Por tanto, el resultado de ~7;
será -8, y el de ~8;
será -9.
El operador << bit a bit
El operador de desplazamiento a la izquierda bit a bit se representa con dos símbolos «menor que» (<<
). Este operador toma los bits de un número y los mueve hacia la izquierda tantas posiciones como se indique en el segundo operando. Por tanto, requiere dos operandos para funcionar.
Cada desplazamiento de una posición hacia la izquierda equivale a multiplicarlo por 2, cada desplazamiento de dos posiciones equivale a multiplicarlo por 4, y así sucesivamente. Por tanto, por cada desplazamiento se duplica el valor del número. Los espacios vacíos de la derecha se rellenan con ceros.
Veamos un ejemplo de desplazamiento del número 5 en varias posiciones hacia la izquierda. Para simplificar la visualización, la tabla solo muestra los bits relevantes, aunque JavaScript trabaja con 32 bits, rellenando el resto con ceros.
Expresión (con << ) | Núm. binario | Núm. decimal |
---|---|---|
5 << 0; | 0101 | 5 |
5 << 1; | 1010 | 10 |
5 << 2; | 10100 | 20 |
5 << 3; | 101000 | 40 |
Como podemos ver en esta tabla, cuantas más posiciones se desplazan los bits del número 5 en sistema binario, más grande es el valor del número decimal resultante, duplicándose cada vez respecto la anterior.
El operador >> bit a bit
El operador de desplazamiento a la derecha bit a bit se representa mediante dos símbolos «mayor que» (>>
). Este operador desplaza los bits de un número hacia la derecha, tantas posiciones como se especifique en el segundo operando. Al igual que el operado anterior, requiere dos operandos.
Cada movimiento de una posición hacia la derecha equivale a dividir el número entre 2 y descartar cualquier resto decimal (redondeo a la baja). Así, por cada movimiento, el valor numérico entero se reduce casi a la mitad.
Dado que este desplazamiento es con signo, el bit que está más a la izquierda —el bit de signo, que es 0 para positivos y 1 para negativos— se preserva y se utiliza para rellenar los huecos que se abren a la izquierda. Esto garantiza que el signo del número no cambie durante la operación.
Vamos a crear un desplazamiento del número 5 en varias posiciones hacia la derecha. Nuevamente, para simplificar la visualización, la siguiente tabla solo muestra los bits relevantes, ya que JS trabaja con 32 bits.
Expresión (con >> ) | Núm. binario | Núm. decimal |
---|---|---|
5 >> 0; | 0101 | 5 |
5 >> 1; | 0010 | 2 |
5 >> 2; | 0001 | 1 |
5 >> 3; | 0000 | 0 |
Tal y como se puede ver en la tabla, a medida que los bits del número 5 se desplazan hacia la derecha, el valor decimal se reduce casi a la mitad en cada iteración: de 5 se pasa a 2, luego a 1 y, finalmente, a 0. Los bits desplazados fuera del rango por la derecha simplemente se descartan.
El operador >>> bit a bit
El operador de desplazamiento a la derecha sin signo se representa con tres símbolos de «mayor que» (>>>
). Este operador también desplaza los bits de un número hacia la derecha según el número de posiciones indicado en el segundo operando. Pero a diferencia de >>
, ignora el bit de signo del número, rellenando los espacios vacíos que se abren a la izquierda con ceros.
Por tanto, este operador es idéntico a >>
para los números positivos. Sin embargo, es muy distinto para los números negativos: el relleno con ceros en la izquierda convierte el número de negativo a positivo.
Vamos a ver un ejemplo con un número negativo: -5. En el formato de 32 bits, el -5 comienza con 1, el bit que indica el signo negativo, mientras que el 5 desplazado comienza con 0, es decir, de signo positivo.
Expresión (con >>> ) | Núm. binario | Núm. decimal |
---|---|---|
-5 >>> 0; | 111…1011 | 4294967291 |
-5 >>> 1; | 011…1101 | 2147483645 |
-5 >>> 2; | 001…1110 | 1073741822 |
-5 >>> 3; | 000…1111 | 536870911 |
Como se observa en la tabla, el operador >>>
tiene un efecto drástico sobre los números negativos. Incluso cuando el desplazamiento es cero (-5 >>> 0;
), el valor original negativo se convierte en un número positivo extremadamente grande (4294967291). A partir de ahí, cada desplazamiento posterior reduce ese gran valor aproximadamente a la mitad.