Descripción de los operadores bit a bit
() translation by (you can also view the original English article)
Los operadores bit a bit son esos operadores de aspecto extraño que pueden parecer difíciles de entender ... pero ya no! Este artículo fácil de seguir le ayudará a entender lo que son y cómo usarlos, con un par de ejemplos prácticos, así para mostrarle cuándo y por qué los necesitaría.
Introducción
Los operadores bit a bit son operadores (al igual que +, *, &&, etc.) que operan en ints y uints en el nivel binario. Esto significa que miran directamente los dígitos binarios o bits de un entero. Todo esto suena aterrador, pero en verdad los operadores bit a bit son bastante fáciles de usar y también bastante útiles!
Es importante, sin embargo, que tenga una comprensión de los números binarios y hexadecimales. Si no lo haces, por favor echa un vistazo a este artículo - que realmente le ayudará! A continuación se muestra una pequeña aplicación que le permitirá probar los diferentes operadores bit a bit.
No te preocupes si aún no entiendes lo que está pasando, todo estará claro pronto...
Reconocer los operadores bit a bit
Echemos un vistazo a los operadores bit a bit que proporciona AS3. Muchos otros lenguajes son bastante similares (por ejemplo, JavaScript y Java tienen operadores prácticamente idénticos):
- & (AND bit a bit)
- | (OR bit a bit)
- ~ (bit a bit NO)
- ^ (XOR bit a bit)
- << (desplazamiento bit a la izquierda)
- >> (desplazamiento a la derecha bit a bit)
- >>> (desplazamiento a la derecha sin signo bit a bit)
- &= (asignación AND bit a bit)
- |= (asignación OR bit a bit)
- ^= (asignación XOR bit a bit)
- <<= (desplazamiento y asignación a la izquierda bit a bit)
- >>= (desplazamiento y asignación a la derecha bit a bit)
- >>>= (desplazamiento y asignación a la derecha sin signo bit a bit)
Hay un par de cosas que debe tomar de esto: En primer lugar, algunos operadores bit a bit se parecen a los operadores que ha utilizado antes (&vs. &&, | vs. ||). Esto se debe a que son algo similares.
En segundo lugar, la mayoría de los operadores bit a bit vienen con una forma de asignación compuesta de sí mismos. Esto es lo mismo que la forma en que puede usar + y +=, - y -=, etc.
El operador &
Arriba primero: el operador AND bit a bit, &. Sin embargo, un aviso rápido: normalmente, los ints y los uints ocupan 4 bytes o 32 bits de espacio. Esto significa que cada int o uint se almacena como 32 dígitos binarios. Por el bien de este tutorial, vamos a pretender a veces que ints y uints sólo ocupan 1 byte y sólo tienen 8 dígitos binarios.
El operador &compara cada dígito binario de dos enteros y devuelve un nuevo entero, con un 1 siempre que ambos números tuvieran un 1 y un 0 en cualquier otro lugar. Un diagrama vale más que mil palabras, así que aquí hay uno para aclarar las cosas. Representa hacer 37 & 23, que es igual a 5.

Observe cómo se comparan cada dígito binario de 37 y 23, y el resultado tiene un 1 siempre que 37 y 23 tuvieran un 1, y el resultado tiene un 0 en caso contrario.
Una forma común de pensar en los dígitos binarios es como true o false. Es decir, 1 es equivalente a true y 0 es equivalente a false. Esto hace que el operador &tenga más sentido.
Cuando comparamos dos booleanos, normalmente hacemos boolean1 && boolean2. Esa expresión solo es true si boolean1 y boolean2 son true. De la misma manera, integer1 & integer2 es equivalente, ya que el operador & solo genera un 1 cuando ambos dígitos binarios de nuestros dos enteros son 1.
Aquí hay una tabla que representa esa idea:

Un pequeño uso del operador &es comprobar si un número es par o impar. Para los enteros, simplemente podemos comprobar el bit situado más a la derecha (también llamado el bit menos significativo) para determinar si el entero es impar o par. Esto se debe a que al convertir a base 10, el bit situado más a la derecha representa 20 o 1. Cuando el bit más a la derecha es 1, sabemos que nuestro número es impar ya que estamos agregando 1 a un montón de potencias de dos que siempre serán pares. Cuando el bit más a la derecha es 0, sabemos que nuestro número será par, ya que simplemente consiste en sumar un montón de números pares.
A continuación se muestra un ejemplo:
1 |
var randInt:int = int(Math.random()*1000); |
2 |
if(randInt & 1) |
3 |
{
|
4 |
trace("Odd number."); |
5 |
}
|
6 |
else
|
7 |
{
|
8 |
trace("Even number."); |
9 |
}
|
En mi computadora, este método era aproximadamente un 66% más rápido que usar randInt % 2 para verificar los números pares e impares. Eso es todo un aumento de rendimiento!
El | operador
A continuación se encuentra el operador OR bit a bit, |. Como habrás adivinado, el | operador es al || como el operador &es para el operador &&. El | operator compara cada dígito binario en dos enteros y devuelve un 1 si cualquiera de ellos es 1. De nuevo, esto es similar a la || operador con booleanos.

Echemos un vistazo al mismo ejemplo que antes, excepto ahora usando el | en lugar del operador &. Ahora estamos haciendo 37 | 23 que es igual a 55:
Banderas: Un uso de la & y | Operadores
Podemos aprovechar el & y | operadores para permitirnos pasar varias opciones a una función en un solo int.
Echemos un vistazo a una posible situación. Estamos creando una clase de ventana emergente. En la parte inferior de la misma, podemos tener un botón Sí, No, Bien o Cancelar o cualquier combinación de esos - ¿cómo debemos hacer esto? Aquí está la manera difícil:
1 |
public class PopupWindow extends Sprite |
2 |
{
|
3 |
// Variables, Constructor, etc...
|
4 |
|
5 |
|
6 |
public static void showPopup(yesButton:Boolean, noButton:Boolean, okayButton:Boolean, cancelButton:Boolean) |
7 |
{
|
8 |
if(yesButton) |
9 |
{
|
10 |
// add YES button
|
11 |
}
|
12 |
|
13 |
if(noButton) |
14 |
{
|
15 |
// add NO Button
|
16 |
}
|
17 |
// and so on for the rest of the buttons
|
18 |
}
|
19 |
}
|
¿Es esto horrible? No. Pero es malo, si eres programador, tener que buscar el orden de los argumentos cada vez que llamas a la función. También es molesto - por ejemplo, si sólo desea mostrar el botón Cancelar, tiene que establecer todos los demás booleanos en false.
Usemos lo que aprendimos acerca de & y | para hacer una mejor solución:
1 |
public class PopupWindow extends Sprite |
2 |
{
|
3 |
public static const YES:int = 1; |
4 |
public static const NO:int = 2; |
5 |
public static const OKAY:int = 4; |
6 |
public static const CANCEL:int = 8; |
7 |
|
8 |
public static void showPopup(buttons:int) |
9 |
{
|
10 |
if(buttons & YES) |
11 |
{
|
12 |
// add YES button
|
13 |
}
|
14 |
|
15 |
if(buttons & NO) |
16 |
{
|
17 |
// add NO button
|
18 |
}
|
19 |
}
|
20 |
}
|
¿Cómo llamaría un programador a la función para que se muestren los botones Sí, No y Cancelar? Así:
1 |
PopupWindow.show(PopupWindow.YES | PopupWindow.NO | PopupWindow.CANCEL); |
¿Qué pasa? Es importante tener en cuenta que nuestras constantes en el segundo ejemplo son todas potencias de dos. Por lo tanto, si nos fijamos en sus formas binarias, notaremos que todos tienen un dígito igual a 1, y el resto igual a 0. De hecho, cada uno de ellos tiene un dígito diferente igual a 1. Esto significa que no importa cómo los combinemos con |, cada combinación nos dará un número único. Mirándolo de una manera diferente, fuera del resultado de nuestro | será un número binario con un 1 siempre que nuestras opciones tuvieran un 1.
Para nuestro ejemplo actual tenemos PopupWindow.YES | PopupWindow.NO | PopupWindow.CANCEL que equivale a 1 | 2 | 8 que reescrito en binario es 00000001 | 00000010 | 00001000 que nos da un resultado de 00001011.
Ahora, en nuestra función showPopup(), usamos & para verificar qué opciones se pasaron. Por ejemplo, cuando marcamos los botones & SÍ, todos los bits en SÍ son iguales a 0 excepto el más a la derecha. Por lo tanto, esencialmente estamos comprobando si el bit más a la derecha en los botones es un 1 o no. Si es así, los botones & YES no serán iguales a 0 y se ejecutará cualquier cosa en la instrucción if. Por el contrario, si el bit situado más a la derecha en los botones es 0, botones & SÍ será igual a 0 y la instrucción if no se ejecutará.
El operador ~
El operador NOT bit a bit es ligeramente diferente de los dos que hemos visto hasta ahora. En lugar de tomar un entero en cada lado de él, toma un entero sólo después de él. Esto es como el ! operador, y, no en vano, hace algo similar. De hecho, al igual que ! voltea un booleano de verdadero a falso o viceversa, el operador ~ invierte cada dígito binario en un entero: de 0 a 1 y de 1 a 0:

Un ejemplo rápido. Digamos que tenemos el entero 37, o 00100101. ~37 es entonces 11011010. ¿Cuál es el valor base 10 de esto? pozo...
Complemento de dos, uint vs int, y Más!
¡Ahora comienza la diversión! Vamos a echar un vistazo más de cerca a los números binarios en una computadora. Empecemos por la uint. Como se mencionó anteriormente, un uint suele tener una longitud de 4 bytes o 32 bits, lo que significa que tiene 32 dígitos binarios. Esto es fácil de entender: para obtener el valor de base 10 simplemente convertimos el número a base 10 regularmente. Siempre obtendremos un número positivo.
Pero, ¿qué hay del int? También utiliza 32 bits, pero ¿cómo almacena los números negativos? Si adivinaste que el primer dígito se usa para almacenar el signo, estás en el camino correcto. Echemos un vistazo al sistema de complemento de los dos para almacenar números binarios. Si bien no entraremos en todos los detalles aquí, se usa el sistema de complemento de dos porque facilita la aritmética binaria.
Para encontrar el complemento de los dos de un número binario, simplemente volteamos todos los bits (es decir, hacemos lo que hace el operador ~) y agregamos uno al resultado. Vamos a probar esto una vez:

A continuación, definimos nuestro resultado como el valor -37. ¿Por qué hacer este proceso complicado y no simplemente voltear el primer bit y llamar a eso -37?
Bueno, tomemos una expresión simple 37 + -37. Todos sabemos que esto debe ser igual a 0, y cuando agregamos el 37 al complemento de sus dos, eso es lo que obtenemos:

Observe que dado que nuestros enteros solo contienen ocho dígitos binarios, el 1 en nuestro resultado se elimina, y terminamos con 0, como deberíamos.
Para recapitular, para encontrar lo negativo de un número, simplemente tomamos el complemento de sus dos. Podemos hacer esto invirtiendo todos los bits y agregando uno.
¿Quieres probar esto tú mismo? Añadir trace(~37+1); a un archivo AS3 y, a continuación, compílelo y ejecútelo. Verás que -37 está impreso, como debería ser.
También hay un pequeño atajo para hacer esto a mano: comenzando por la derecha, trabajar a la izquierda hasta llegar a un 1. Voltee todos los bits a la izquierda de este primer 1.

Cuando estamos viendo un número binario con signo (en otras palabras, uno que puede ser negativo, un int no un uint), podemos mirar el dígito más a la izquierda para saber si es negativo o positivo. Si es un 0, entonces el número es positivo y podemos convertir a base 10 simplemente calculando su valor de base 10. Si el bit situado más a la izquierda es un 1, entonces el número es negativo, por lo que tomamos el complemento del número de los dos para obtener su valor positivo y luego simplemente agregamos un signo negativo.
Por ejemplo, si tenemos 11110010, sabemos que es un número negativo. Podemos encontrar su complemento de dos volteando todos los dígitos a la izquierda del 1 más a la derecha, dándonos 00001110. Esto es igual a 13, por lo que sabemos que 11110010 es igual a -13.
El operador ^
Volvemos a los operadores bit a bit, y el siguiente es el operador XOR bit a bit. No hay ningún operador booleano equivalente a éste.
El operador ^ es similar a & y | operadores en que toma un int o uint en ambos lados. Cuando está calculando el número resultante, vuelve a comparar los dígitos binarios de estos números. Si uno u otro es un 1, insertará un 1 en el resultado, de lo contrario insertará un 0. Aquí es donde proviene el nombre XOR, o "exclusivo o".

Echemos un vistazo a nuestro ejemplo habitual:



El operador ^ tiene usos - es especialmente bueno para alternar dígitos binarios - pero no cubriremos ninguna aplicación práctica en este artículo.
El operador <<
Ahora estamos en los operadores de cambio de bits, específicamente el operador de desplazamiento a la izquierda bit a bit aquí.
Estos funcionan de manera un poco diferente que antes. En lugar de comparar dos enteros como &, | y ^ hizo, estos operadores desplazan un entero. En el lado izquierdo del operador está el entero que se está desplazando, y a la derecha está cuánto se debe desplazar. Así, por ejemplo, 37 << 3 está desplazando el número 37 a la izquierda por 3 lugares. Por supuesto, estamos trabajando con la representación binaria de 37.
Echemos un vistazo a este ejemplo (recuerde, solo vamos a fingir que los enteros solo tienen 8 bits en lugar de 32). Aquí tenemos el número 37 sentado en su bonito bloque de memoria de 8 bits de ancho.



Bien, deslicemos todos los dígitos hacia la izquierda por 3, como lo harían 37 << 3:



Pero ahora tenemos un pequeño problema: ¿qué hacemos con los 3 bits abiertos de memoria desde donde movimos los dígitos?



¡Claro! Los puntos vacíos se reemplazan con 0s. Terminamos con 00101000 . Y eso es todo lo que hay a la izquierda. Tenga en cuenta que Flash siempre piensa que el resultado de un cambio de bits izquierdo es un int, no un uint. Así que si necesitas un uint por alguna razón, tendrás que lanzarlo a un uint como este: uint(37 << 3). Esta conversión en realidad no cambia ninguna de la información binaria, solo cómo flash la interpreta (la cosa del complemento de los dos enteros).
Una característica interesante del cambio de bits izquierdo es que es lo mismo que multiplicar un número por dos a la potencia shiftAmount-th. Así, 37 << 3 == 37 * Math.pow(2,3) == 37 * 8. Si puede usar el turno izquierdo en lugar de Math.pow, verá un gran aumento del rendimiento.
Es posible que haya notado que el número binario con el que terminamos no era igual a 37 * 8. Esto es sólo de nuestro uso de sólo 8 bits de memoria para enteros; si lo prueba en ActionScript, obtendrá el resultado correcto. O bien, ¡pruébalo con la demo en la parte superior de la página!
El operador de >>
Ahora que entendemos el bitshift izquierdo, el siguiente, el bitshift derecho, será fácil. Todo se desliza hacia la derecha la cantidad que especificamos. La única pequeña diferencia es lo que los bits vacíos se llenan con.
Si estamos empezando con un número negativo (un número binario donde el bit situado más a la izquierda es un 1), todos los espacios vacíos se llenan con un 1. Si comenzamos con un número positivo (donde el bit más a la izquierda, o el bit más significativo, es un 0), entonces todos los espacios vacíos se llenan con un 0. De nuevo, todo esto se remonta al complemento de dos.
Si bien esto suena complicado, básicamente solo conserva el signo del número con el que comenzamos. Así que -8 >> 2 == -2 mientras que 8 >> 2 == 2. Recomendaría probarlos en papel usted mismo.
Dado que >> es lo opuesto a <<, no es sorprendente que cambiar un número a la derecha sea lo mismo que dividirlo por 2 a la potencia de shiftAmount. Es posible que haya notado esto en el ejemplo anterior. De nuevo, si puede usar esto para evitar llamar a Math.pow, obtendrá un aumento significativo del rendimiento.
El operador >>>
Nuestro operador bit a bit final es el desplazamiento a la derecha sin signo bit a bit. Esto es muy similar al desplazamiento a la derecha bit a bit regular, excepto que todos los bits vacíos a la izquierda se rellenan con 0s. Esto significa que el resultado de este operador siempre es un entero positivo y siempre trata el entero que se desplaza como un entero sin signo. No vamos a ejecutar a través de un ejemplo de esto en esta sección, pero vamos a ver un uso para él muy pronto.
Usar operadores bit a bit para trabajar con colores
Uno de los usos más prácticos de los operadores bit a bit en Actionscript 3 es trabajar con colores, que normalmente se almacenan como uints.
El formato estándar para los colores es escribirlos en hexadecimal: 0xAARRGGBB - cada letra representa un dígito hexadecimal. Aquí, los dos primeros dígitos hexadecimales, que son equivalentes a los primeros ocho dígitos binarios, representan nuestro alfa o transparencia. Los siguientes ocho bits representan la cantidad de rojo en nuestro color (por lo que un entero de 0 a 255), los siguientes ocho la cantidad de verde, y los ocho finales representan la cantidad de azul en nuestro color.
Sin operadores bit a bit, es extremadamente difícil trabajar con colores en este formato, ¡pero con ellos es fácil!
Desafío 1: Encontrar la cantidad de azul en un color: Usando el operador &, intente encontrar la cantidad de azul en un color arbitrario.
1 |
public function findBlueComponent(color:uint):uint |
2 |
{
|
3 |
// Your code here!
|
4 |
}
|
Necesitamos una forma de 'borrar' o enmascarar todos los demás datos en color y simplemente tener el componente azul a la izquierda. Esto es fácil, en realidad! Si tomamos color &0x000000FF - o, escrito más simplemente, color &0xFF - terminamos con sólo el componente azul.



Como puede ver desde arriba y aprendió en la descripción del operador &, cualquier dígito binario &0 siempre será igual a 0, mientras que cualquier dígito binario &1 mantendrá su valor. Entonces, si enmascaramos nuestro color 0xFF que solo tiene 1s donde se encuentra el componente azul de nuestro color, terminamos con solo el componente azul.
Desafío 2: Encontrar la cantidad de rojo en un color: Usando dos operadores bit a bit, intente encontrar la cantidad de rojo en un color arbitrario.
1 |
public function findRedComponent(color:uint):uint |
2 |
{
|
3 |
// Your code here!
|
4 |
}
|
En realidad, tenemos dos soluciones a este problema. Uno sería volver (color &0xFF0000) >> 16; y el otro sería return (color >> 16) & 0xFF;
Esto es muy similar al Desafío 1, excepto que tenemos que cambiar nuestra respuesta en algún momento.
Desafío 3: Encontrar la transparencia de un color: Usando solo un operador bit a bit, intente encontrar el alfa de un color (un entero de 0 a 255).
1 |
public function findAlphaComponent(color:uint):uint |
2 |
{
|
3 |
// Your code here!
|
4 |
}
|
Este es un toque más complicado. Tenemos que tener cuidado con el operador de turno derecho que elegimos. Dado que estamos trabajando con los dígitos situados más a la izquierda de un uint, queremos usar >>> operador. Por lo tanto, nuestra respuesta simplemente es el color de retorno >>> 24;.
Desafío final: Crear un color a partir de sus componentes: Usar el << y | operadores, tomar los componentes de un color y combinarlos en un uint.
1 |
public function createColor(a:uint, r:uint, g:uint, b:uint):uint |
2 |
{
|
3 |
// Your code here!
|
4 |
}
|
Aquí, tenemos que cambiar cada componente a su posición correcta, y luego fusionarlos. Queremos que Flash lo trate como un entero sin signo, por lo que lo lanzamos a un uint: return uint((a << 24) | r << 16) | g << 8) | b);
Operadores compuestos
Es posible que haya notado que he descuidado explicar los operadores bit a bit compuestos. Imaginemos que tenemos un entero x. Entonces, x = x & 0xFF es lo mismo que x &= 0xFF, x = x | 256 es lo mismo que x |= 256, y así sucesivamente para el resto de los operadores compuestos.
conclusión
Gracias por leer este artículo! Espero que ahora entienda los operadores bit a bit y pueda utilizarlos en su código AS3 (¡o en muchos otros lenguajes!). Como siempre, si tiene alguna pregunta o comentario, por favor déjelo a continuación.