De binario a byte y de byte a binario

PostedEn Programación     Comments 1 comentario
Apr
25

Algunas veces nos podemos encontrar intentando convertir cadenas de caracteres en binario a su correspondiente byte.
Por ejemplo, tenemos un String con el siguiente texto “11000111″ y lo queremos convertir a un byte (que corresponde con 199 en decimal, si utilizamos una calculadora cualquiera que nos convierta de binario a decimal).
?Y qué sucede? Pues que no funciona y empezamos a pensar cosas raras sobre si los byte en Java son con signo o sin signo y demás suposiciones. Aclaremos todo esto viendo cómo falla y por qué.

Primer intento. Así, sin mirar la documentación ni nada, podemos probar con el método decode de Byte, que tiene toda la pinta de servir para esto.

String bb = "11000111";
Byte BB = Byte.decode(bb);

No funciona, ya que el método decode solo acepta cadenas en decimal, hexadecimal o en octal (ahora si nos leemos la documentación).
Y esto lo vemos claramente ya que la excepción que arroja:
java.lang.NumberFormatException: Value out of range. Value:”11000111″ Radix:10
nos indica que esta parseando el número en base 10 (radix:10), es decir, intenta convertir el 11000111 en decimal (11.000.111) y no en binario como queremos.

Segundo intento. Ahora probamos con el método parseByte, que admite como parámetro la base (radix) del número original a parsear. Como es un número binario, probamos con la base 2.

String bb = "11000111";
Byte BB = new Byte(Byte.parseByte(bb, 2));

Y nos devuelve un fantástico java.lang.NumberFormatException: Value out of range. Value:”11000111″ Radix:2.
?Cómo es posible esto, si ahora hemos utilizado el método correcto y hemos especificado explícitamente que es base 2?
El problema es que en Java, los byte (y los int) se guardan con signo, es decir, el bit de mayor peso (el situado en la primera posición empezando por la izquierda) indica 1 si el número es negativo y 0 si es positivo, por lo que no se guardan valores del 0 al 254, sino del -128 al 127 incluido. (Ver Java Language Specification: Integral Types and Values
Y nuestro número original que queremos convertir “11000111″ no respeta, ni tiene porqué respetar, esta especificación. Es un número binario positivo que puede tener cualquier longitud. Y, si fuera negativo, se especificaría directamente con un signo menos delante.
Por ejemplo, esta conversión si que funciona, ya que los dos números están dentro del límite -128/127

String positivo = "111";
String negativo = "-111";
Byte BBp = new Byte(Byte.parseByte(positivo, 2));
Byte BBm = new Byte(Byte.parseByte(negativo, 2));

Para solucionar esto solo tenemos que convertir nuestro número en binario temporalmente a un primitivo de tamaño superior (short o int) y luego hacemos un casting a byte.

String bb = "11000111";
short ss = Short.parseShort(bb, 2);
Byte BB = new Byte((byte)ss);

// O tambien sin dar tantas vueltas
byte bait = (byte)Short.parseShort(bb, 2);

Ahora nos falta el caso inverso. Tenemos nuestro byte y queremos pasarlo a su cadena binaria correspondiente. Pero resulta que la única clase que tiene un método que haga exactamente esto es la clase Integer, método toBinaryString.
Si utilizamos este método, obtenemos una cadena demasiado larga que no se corresponde con nuestra cadena original.

String bb = "11000111";
byte bait = (byte)Short.parseShort(bb, 2);
String s = Integer.toBinaryString(bait);
System.out.println(s);

Este código nos devuelve “11111111111111111111111111000111″. Esto sucede así al promocionar de byte a int (la explicación de esto la haremos otro dia). Así que tenemos que eliminar de nuestro byte los “1″ iniciales para quedarnos solo con las 8 últimas posiciones. Para esto utilizamos la máscara 0xFF (o lo que es lo mismo: 11111111 en binario o 255 en decimal) y hacemos un and binario con el operador &.

String bb = "11000111";
byte bait = (byte)Short.parseShort(bb, 2);
String s = Integer.toBinaryString(bait & 0xFF);
System.out.println(s);

Ahora si que obtenemos la cadena binaria original. Y con esto ya podemos resumir las operaciones a realizar para convertir una cadena en binario a byte y viceversa. Para lo primero, solo tenemos que utilizar el método parseShort de la clase Short (o el método parseInt de la clase Integer…) y hacer un casting a byte. Y para lo segundo, utilizaremos el método toBinaryString de la clase Integer pero haciendo un and binario con 0xFF.

Categorías