Cómo convertir Int a Byte sin firmar y viceversa

92

Necesito convertir un número en un byte sin firmar. El número siempre es menor o igual a 255, por lo que cabe en un byte.

También necesito convertir ese byte nuevamente en ese número. ¿Cómo haría eso en Java? He intentado varias formas y ninguna funciona. Esto es lo que estoy tratando de hacer ahora:

int size = 5;
// Convert size int to binary
String sizeStr = Integer.toString(size);
byte binaryByte = Byte.valueOf(sizeStr);

y ahora para convertir ese byte nuevamente en el número:

Byte test = new Byte(binaryByte);
int msgSize = test.intValue();

Claramente, esto no funciona. Por alguna razón, siempre convierte el número en 65. ¿Alguna sugerencia?

cielo oscuro
fuente
También probé este método: crazysquirrel.com/computing/java/basics/… - no funciona.
darksky

Respuestas:

201

Un byte siempre está firmado en Java. Sin embargo, puede obtener su valor sin firmar haciendo un and binario con 0xFF:

int i = 234;
byte b = (byte) i;
System.out.println(b); // -22
int i2 = b & 0xFF;
System.out.println(i2); // 234
JB Nizet
fuente
Todavía tengo 65 ... ¿Por qué?
Darksky
1
Está almacenando el número que desea en el byte. Java simplemente lo considera como un número firmado en lugar de uno sin firmar. Pero cada bit es igual como si fuera un número sin firmar. La conversión de su cadena en bytes es otra pregunta no relacionada. Publíquelo por separado, en una nueva pregunta.
JB Nizet
1
use getInt para recuperar su int y transforme el int como un byte. Un int es un int. No importa de dónde venga.
JB Nizet
19
¿Por qué bit a bit y with 0xFFdan como resultado un entero sin signo? Alguna explicación sería realmente útil.
user462455
2
Java 8 usa el mismo método: public static int toUnsignedInt (byte x) {return ((int) x) & 0xff; }
Daniel De León
55

Java 8 permite Byte.toUnsignedIntconvertir bytea intpor conversión sin firmar. En el JDK de Oracle, esto simplemente se implementa return ((int) x) & 0xff;porque HotSpot ya comprende cómo optimizar este patrón, pero podría estar intrinsificado en otras VM. Más importante aún, no se necesitan conocimientos previos para comprender lo que hace una llamada a toUnsignedInt(foo).

En total, Java 8 proporciona métodos para convertir bytey shorta unsigned inty long, y inta unsigned long. Un método para convertir bytea firmar shortse omitió deliberadamente porque la JVM sólo proporciona aritmética sobre inty longde todos modos.

Para convertir un int volver a un byte, sólo tiene que utilizar un yeso: (byte)someInt. La conversión primitiva de estrechamiento resultante descartará todos menos los últimos 8 bits.

Jeffrey Bosboom
fuente
7

Si solo necesita convertir un valor esperado de 8 bits de un int con signo a un valor sin firmar, puede usar el desplazamiento de bits simple:

int signed = -119;  // 11111111 11111111 11111111 10001001

/**
 * Use unsigned right shift operator to drop unset bits in positions 8-31
 */
int psuedoUnsigned = (signed << 24) >>> 24;  // 00000000 00000000 00000000 10001001 -> 137 base 10

/** 
 * Convert back to signed by using the sign-extension properties of the right shift operator
 */
int backToSigned = (psuedoUnsigned << 24) >> 24; // back to original bit pattern

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

Si utiliza algo diferente al inttipo base, obviamente deberá ajustar la cantidad de cambio: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

Además, tenga en cuenta que no puede usar el bytetipo, si lo hace, dará como resultado un valor firmado como lo mencionaron otros respondedores. El tipo primitivo más pequeño que podría usar para representar un valor sin signo de 8 bits sería a short.

dm78
fuente
3

La Integer.toString(size)llamada se convierte en la representación char de su entero, es decir, el char '5'. La representación ASCII de ese carácter es el valor 65.

Primero debe analizar la cadena a un valor entero, por ejemplo, usando Integer.parseInt , , para recuperar el valor int original.

Como conclusión, para una conversión firmada / sin firmar, es mejor dejar Stringfuera de la imagen y usar la manipulación de bits como sugiere @JB.

Péter Török
fuente
¿Así que así? (Todavía obteniendo 65 aquí)String sizeStr = Integer.toString(size); int sizeInt = Integer.parseInt(sizeStr); byte binaryByte = Byte.valueOf((byte) sizeInt);
darksky
@Nayefc, actualicé mi respuesta justo cuando estaba escribiendo su comentario :-)
Péter Török
¡OK gracias! Sin embargo, por alguna razón TODAVÍA imprime 65. ¿Qué significa esto? Si solo muestra 65, lo que realmente se está almacenando en el byte, entonces no lo entiendo. También verifique mi pregunta, la
edité
@Nayefc, nuevamente, String.getBytes()produce los valores ASCII de los caracteres contenidos en el texto, no los valores numéricos de los dígitos. Es necesario para analizar la cadena en un valor numérico, como he indicado anteriormente.
Péter Török
1
@Nayefc, entonces esa es en realidad una pregunta completamente diferente. Y AFAIS debería funcionar. Cree una nueva pregunta, incluida la entrada y salida reales de la conversión, y el código completo para reproducir el caso.
Péter Török
3

La solución funciona bien (¡gracias!), Pero si desea evitar la transmisión y dejar el trabajo de bajo nivel al JDK, puede usar un DataOutputStream para escribir sus int y un DataInputStream para volver a leerlos. Se tratan automáticamente como sin firmar bytes entonces:

Para convertir int's en bytes binarios;

ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
int val = 250;
dos.write(byteVal);
...
dos.flush();

Leyendo de nuevo en:

// important to use a (non-Unicode!) encoding like US_ASCII or ISO-8859-1,
// i.e., one that uses one byte per character
ByteArrayInputStream bis = new ByteArrayInputStream(
   bos.toString("ISO-8859-1").getBytes("ISO-8859-1"));
DataInputStream dis = new DataInputStream(bis);
int byteVal = dis.readUnsignedByte();

Esp. útil para manejar formatos de datos binarios (por ejemplo, formatos de mensajes planos, etc.)

Gregor
fuente
3

Excepto char, todos los demás tipos de datos numéricos en Java están firmados.

Como se dijo en una respuesta anterior, puede obtener el valor sin firmar realizando una andoperación con 0xFF. En esta respuesta, voy a explicar cómo sucede.

int i = 234;
byte b = (byte) i;
System.out.println(b);  // -22

int i2 = b & 0xFF;      
// This is like casting b to int and perform and operation with 0xFF

System.out.println(i2); // 234

Si su máquina es de 32 bits, entonces el inttipo de datos necesita 32 bits para almacenar valores. bytesolo necesita 8 bits.

La intvariable ise representa en la memoria de la siguiente manera (como un entero de 32 bits).

0{24}11101010

Entonces la bytevariable bse representa como:

11101010

Como bytes no están firmados, este valor representa-22 . (Busque el complemento de 2 para aprender más sobre cómo representar números enteros negativos en la memoria)

Entonces, si lanzas es int, seguirá siendo -22porque el lanzamiento conserva el signo de un número.

1{24}11101010

El 32-bitvalor de conversión de brealizar la andoperación con 0xFF.

 1{24}11101010 & 0{24}11111111
=0{24}11101010

Entonces obtienes 234la respuesta.

Ramesh-X
fuente
1

Manejo de bytes y enteros sin signo con BigInteger:

byte[] b = ...                    // your integer in big-endian
BigInteger ui = new BigInteger(b) // let BigInteger do the work
int i = ui.intValue()             // unsigned value assigned to i
pgrandjean
fuente
1

Aunque es demasiado tarde, me gustaría dar mi opinión sobre esto, ya que podría aclarar por qué funciona la solución proporcionada por JB Nizet. Me encontré con este pequeño problema trabajando en un analizador de bytes y en la conversión de cadenas yo mismo. Cuando copia de un tipo integral de mayor tamaño a un tipo integral de menor tamaño, ya que este documento de Java dice que esto sucede:

https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3 Una conversión de restricción de un entero con signo a un tipo integral T simplemente descarta todo menos el n más bajo bits de orden, donde n es el número de bits utilizados para representar el tipo T.Además de una posible pérdida de información sobre la magnitud del valor numérico, esto puede provocar que el signo del valor resultante difiera del signo del valor de entrada .

Puede estar seguro de que un byte es un tipo integral, ya que este documento de Java dice https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html byte: el tipo de datos de byte es un dos de 8 bits firmado complemento entero.

Entonces, en el caso de convertir un número entero (32 bits) en un byte (8 bits), simplemente copie los últimos (8 bits menos significativos) de ese número entero en la variable de byte dada.

int a = 128;
byte b = (byte)a; // Last 8 bits gets copied
System.out.println(b);  // -128

La segunda parte de la historia involucra cómo los operadores unarios y binarios de Java promueven los operandos. https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2 La conversión primitiva de ampliación (§5.1.2) se aplica para convertir uno o ambos operandos como se especifica por las siguientes reglas:

Si alguno de los operandos es de tipo double, el otro se convierte en double.

De lo contrario, si alguno de los operandos es de tipo flotante, el otro se convierte en flotante.

De lo contrario, si alguno de los operandos es de tipo long, el otro se convierte en long.

De lo contrario, ambos operandos se convierten al tipo int.

Tenga la seguridad de que si está trabajando con el tipo integral int y / o inferior, se promoverá a un int.

// byte b(0x80) gets promoted to int (0xFF80) by the & operator and then
// 0xFF80 & 0xFF (0xFF translates to 0x00FF) bitwise operation yields 
// 0x0080
a = b & 0xFF;
System.out.println(a); // 128

También me rasqué la cabeza con esto :). Hay una buena respuesta para esto aquí por rgettman. ¿Operadores bit a bit en java solo para enteros y largos?

KaziQ
fuente
0

Si desea utilizar las clases contenedoras primitivas, esto funcionará, pero todos los tipos de Java están firmados de forma predeterminada.

public static void main(String[] args) {
    Integer i=5;
    Byte b = Byte.valueOf(i+""); //converts i to String and calls Byte.valueOf()
    System.out.println(b);
    System.out.println(Integer.valueOf(b));
}
Romeo
fuente
-1

en java 7

public class Main {
    public static void main(String[] args) {
        byte b =  -2;
        int i = 0 ;
        i = ( b & 0b1111_1111 ) ;
        System.err.println(i);
    }
}

resultado: 254

Tristan De Smet
fuente