Operaciones a nivel de bit en Java

Cojín binarioEntrada movida a Inspired Geek.

Las operaciones a nivel de bit no son muy habituales. De hecho es posible que aunque las hayáis visto en un código, no hayáis sabido muy bien lo que hacian. Estos operadores se suelen encontrar en algoritmos de encriptación, en operaciones en las que se precise rapidez y/o eficacia, en el manejo de los pixeles de imágenes, etc.

Normalmente su uso esta cubierto en la mayoría de libros de Java, pero se suelen pasar por alto por que su relevancia en un código suele ser mucho menor que la del resto de operadores. En esta entrada voy a intentar explicar un poco su funcionamiento con ejemplos reales de uso.

Operadores de desplazamiento

Los operadores de desplazamiento a nivel de bits, desplazan ‘n’ bits a la izquierda o la derecha. Esto es lo mismo que decir que multiplicamos el número por 2^n o lo dividimos entre 2^n:

10d << 2 = 1010b << 2 = 101000b = 40d
10d >> 2 = 1010b >> 2 = 0010b = 2d

Notar que las letras ‘d’ y ‘b’ solo indican, respectivamente, si es decimal o binario.

Como veis en el primer caso, hemos desplazado los bits del número 10 dos veces a la izquierda (multiplicar por 2^2, igual a 4), lo que nos da por resultado 40. En el segundo caso hemos desplazado los bits hacia la derecha (dividir por 4), dándonos como resultado 2.

Operador de negación o complemento unario

El operador de negación (~) invierte los bits del número sobre el que opera, sustituyendo los unos por ceros y los ceros por unos:

~10d = ~1010b = 0101b = 5d

Operadores lógicos

En Java tenemos tres operadores lógicos. El primero, el operador AND (&), da resultado 1 si los bits de los dos operarandos son 1 y 0 en el resto de casos:

Función lógica &:
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

Ej. 10d & 2d = 1010b & 0010b = 0010b = 2d

El siguiente operador, OR (|), da como resultado 1 si el bit de alguno de los operandos es 1 y cero si no:

Función lógica |:
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

Ej. 10d | 2d = 1010b | 0010b = 1010b = 10d

Por último, el operador O exlusiva (^), devuelve 1 si uno de los bits de un operando es cero y el del otro es 1. Si esto no se cumple, devuelve 0:

Función lógica ^:
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

Ej. 10d ^ 2d = 1010b ^ 0010b = 1000b = 8d

Ejemplos prácticos de uso

Seguramente a estas alturas sigas sin ver la utilizad de las operaciones a nivel de bits, así que voy a poner dos ejemplos prácticos.

El primer ejemplo esta extraido del código de la clase HashMap de Java. El código es el siguiente:

int capacity = 1;
while (capacity < initialCapacity)
       capacity <<= 1;

El objetivo de este código es buscar una capacidad que sea potencia de 2. Asi, si la capacidad inicial que le pasamos a la clase es, por ejemplo, diez, el código irá desplazando los bits del número uno hacia la izquierda hasta que la capacidad sea mayor o igual a la capacidad inicial. Es resultado una vez salgamos del bucle será 16:

Primera iteración: (1d = 00001b) < 10, luego operamos sobre él: 1d << 1 = 2d
Segunda iteración: (2d = 00010b) < 10, luego operamos sobre él: 2d << 1 = 4d
Tercera iteración: (4d = 00100b) < 10, luego operamos sobre él: 4d << 1 = 8d
Cuarta iteración: (8d = 01000b) < 10, luego operamos sobre él: 8d << 1 = 16d
Quinta iteración: (16d = 10000b) > 10, luego salimos del bucle.

Este código, aunque en principio pueda resultar difícil de comprender, es más eficiente que ir multiplicando por dos.

En el segundo ejemplo se usan los operadores binarios para pasar atributos a un método. Imaginar que tenemos una clase que, a la hora de instanciarla, acepta que se le pase un atributo para definir su funcionamiento. Asi pues, consta de dos variables estaticas. La primera se llama TRABAJAR_LOCAL, su valor es 10000000 y sirve para que la clase no se conecte a la red. La segunda variable se llama ENCRIPTACION_MD5, su valor es 00001000 y su función es encriptar las contraseñas, con las que trabaje la clase, en MD5.

Con todo esto, si quisieramos usar esta clase en modo local y con encriptación MD5, no podriamos, ya que la clase solo admite un atributo. Una opción sería usar métodos para activar estas opciones individualmente. Esta opción presentará un problema de eficiencia si tenemos que activar muchos modos de funcionamiento. Otra opción sería tener una variable LOCAL_MD5, que sea la unión de las dos, pero tendriamos que crear una variable para cada combinación posible, lo cual, aunque posible, es inhumano. La mejor opción es instanciar la clase de la siguiente manera:

Clase c = new Clase(TRABAJAR_LOCAL | ENCRIPTACION_MD5);

Con esto, nuesta variable modo definida en la clase, contendra el valor 10001000 (ver operador OR), el cual es la “unión” de los dos modos de funcionamiento. Ahora solo nos quedaría comprobar dentro de la clase los modos que están activados y realizar las operaciones pertinentes.

Este último ejemplo de uso es muy típico y se suele ver mucho en la API gráfica Java 3D.

Bueno, por mi parte eso es todo. Espero que os haya gustado y sobre todo que os sea útil a la hora de implementar vuestras clases y algoritmos usando operaciones a nivel de bit.

Saludines ;)

  1. Aún no hay trackbacks

Deja un comentario

Fill in your details below or click an icon to log in:

Logo de WordPress.com

You are commenting using your WordPress.com account. Log Out / Cambiar )

Twitter picture

You are commenting using your Twitter account. Log Out / Cambiar )

Facebook photo

You are commenting using your Facebook account. Log Out / Cambiar )

Connecting to %s

Seguir

Get every new post delivered to your Inbox.