¿Podemos hacer un byte sin firmar en Java?

185

Estoy tratando de convertir un byte firmado en unsigned. El problema es que los datos que estoy recibiendo no están firmados y Java no admite el byte sin firmar, por lo que cuando lee los datos, los trata como firmados.

Intenté convertirlo con la siguiente solución que obtuve de Stack Overflow.

public static int unsignedToBytes(byte a)
{
    int b = a & 0xFF;
    return b;
}

Pero cuando nuevamente se convierte en byte, obtengo los mismos datos firmados. Estoy tratando de usar estos datos como un parámetro para una función de Java que acepta solo un byte como parámetro, por lo que no puedo usar ningún otro tipo de datos. ¿Como puedo solucionar este problema?

dln
fuente
2
Guayaba: UnsignedBytes.toint (valor de byte)
jacktrades
20
java.lang.Byte.toUnsignedInt (valor de byte);
themarketka

Respuestas:

107

No estoy seguro de entender tu pregunta.

Acabo de probar esto y para el byte -12 (valor con signo) devolvió el entero 244 (equivalente al valor de byte sin signo pero escrito como int):

  public static int unsignedToBytes(byte b) {
    return b & 0xFF;
  }

  public static void main(String[] args) {
    System.out.println(unsignedToBytes((byte) -12));
  }

¿Es lo que quieres hacer?

Java no permite expresar 244 como un bytevalor, como lo haría C. Para expresar enteros positivos arriba Byte.MAX_VALUE(127) tienes que usar otro tipo de entero, como short, into long.

Guillaume
fuente
1
byte b = (byte)unsignedToBytes((byte) -12); ahora intenta imprimir b
Jigar Joshi el
101
¿Por qué has aceptado esto como la respuesta correcta? Todo lo que hace es exactamente el mismo que el método que menciona en su pregunta: convertir un byte en un entero sin signo.
Adamski
1
A veces es importante tener valores con signo, a veces sin signo, así que probablemente esta es la razón por la que aceptó esta respuesta. (byte) (b & 0xff) no tiene ningún sentido, pero (byte) (Math.min ((b & 0xff) * 2, 255)) tiene sentido, por ejemplo, en gráficos de computadora solo hará que el pixelado esté representado por el byte dos veces más brillante. :-)
iirekm
3
También podría llamarse byteToUnsigned
Hernán Eche
195

El hecho de que las primitivas estén firmadas en Java es irrelevante para la forma en que están representadas en la memoria / tránsito: un byte tiene solo 8 bits y depende de usted si lo interpreta como un rango con signo. No hay una bandera mágica que diga "esto está firmado" o "esto no está firmado".

A medida que se firman las primitivas, el compilador de Java le impedirá asignar un valor superior a +127 a un byte (o inferior a -128). Sin embargo, no hay nada que te impida rechazar un int (o short) para lograr esto:

int i = 200; // 0000 0000 0000 0000 0000 0000 1100 1000 (200)
byte b = (byte) 200; // 1100 1000 (-56 by Java specification, 200 by convention)

/*
 * Will print a negative int -56 because upcasting byte to int does
 * so called "sign extension" which yields those bits:
 * 1111 1111 1111 1111 1111 1111 1100 1000 (-56)
 *
 * But you could still choose to interpret this as +200.
 */
System.out.println(b); // "-56"

/*
 * Will print a positive int 200 because bitwise AND with 0xFF will
 * zero all the 24 most significant bits that:
 * a) were added during upcasting to int which took place silently
 *    just before evaluating the bitwise AND operator.
 *    So the `b & 0xFF` is equivalent with `((int) b) & 0xFF`.
 * b) were set to 1s because of "sign extension" during the upcasting
 *
 * 1111 1111 1111 1111 1111 1111 1100 1000 (the int)
 * &
 * 0000 0000 0000 0000 0000 0000 1111 1111 (the 0xFF)
 * =======================================
 * 0000 0000 0000 0000 0000 0000 1100 1000 (200)
 */
System.out.println(b & 0xFF); // "200"

/*
 * You would typically do this *within* the method that expected an 
 * unsigned byte and the advantage is you apply `0xFF` only once
 * and than you use the `unsignedByte` variable in all your bitwise
 * operations.
 *
 * You could use any integer type longer than `byte` for the `unsignedByte` variable,
 * i.e. `short`, `int`, `long` and even `char`, but during bitwise operations
 * it would get casted to `int` anyway.
 */
void printUnsignedByte(byte b) {
    int unsignedByte = b & 0xFF;
    System.out.println(unsignedByte); // "200"
}
Adamski
fuente
55
Para muchas operaciones no hace diferencia, sin embargo, para algunas operaciones lo hace. De cualquier manera, puede usar un byte como unsigned, o usar char que no está firmado.
Peter Lawrey el
62
Acceder a una matriz con un número potencialmente negativo no es irrelevante.
Stefan
3
@Stefan: quise decir irrelevante en el contexto de cómo están representados en el cable.
Adamski
66
Lo cual es algo irrelevante para la pregunta. Como mencionó que necesita pasarlo a una función que solo acepta parámetros de byte, no importa el clima, lo interpretamos como la representación de byte de un unicornio. Java siempre lo tratará como un número con signo, lo que puede ser problemático para un ejemplo cuando esta función utiliza el parámetro como índice. Sin embargo, para ser justos, también rechacé las otras 2 respuestas principales, ya que tampoco responden la pregunta.
Stefan
2
@Stefan +1 para ti. Absolutamente relevante si está utilizando el byte para acceder a una matriz de 256 elementos. Ese es un excelente ejemplo para demostrar por qué todos deberían comenzar a aprender C y C ++ antes de pasar a Java o C #
Gianluca Ghettini
46

Guía completa para trabajar con bytes sin firmar en Java:

Byte sin firmar en Java

(Fuente de esta respuesta).


El lenguaje Java no proporciona nada parecido a la unsignedpalabra clave. A de byteacuerdo con la especificación del lenguaje representa un valor entre −128 y 127. Por ejemplo, si bytese emite a intJava interpretará el primer bit como signo y usará la extensión de signo .

Dicho esto, nada le impide ver bytesimplemente 8 bits e interpretar esos bits como un valor entre 0 y 255. Solo tenga en cuenta que no hay nada que pueda hacer para forzar su interpretación sobre el método de otra persona. Si un método acepta a byte, entonces ese método acepta un valor entre -128 y 127 a menos que se indique explícitamente lo contrario.

Aquí hay un par de conversiones / manipulaciones útiles para su conveniencia:

Conversiones a / desde int

// From int to unsigned byte
int i = 200;                    // some value between 0 and 255
byte b = (byte) i;              // 8 bits representing that value

// From unsigned byte to int
byte b = 123;                   // 8 bits representing a value between 0 and 255
int i = b & 0xFF;               // an int representing the same value

(O, si tienes Java 8+, úsalo Byte.toUnsignedInt).

Análisis / formateo

La mejor manera es usar las conversiones anteriores:

// Parse an unsigned byte
byte b = (byte) Integer.parseInt("200");

// Print an unsigned byte
System.out.println("Value of my unsigned byte: " + (b & 0xFF));

Aritmética

La representación de 2 complementos "simplemente funciona" para sumar, restar y multiplicar:

// two unsigned bytes
byte b1 = (byte) 200;
byte b2 = (byte) 15;

byte sum  = (byte) (b1 + b2);  // 215
byte diff = (byte) (b1 - b2);  // 185
byte prod = (byte) (b2 * b2);  // 225

La división requiere la conversión manual de operandos:

byte ratio = (byte) ((b1 & 0xFF) / (b2 & 0xFF));
aioobe
fuente
1
'char' no representa un número.
cierre de sesión el
26
En pocas palabras: estás equivocado .
aioobe
36

No hay bytes primitivos sin signo en Java. Lo habitual es lanzarlo a un tipo más grande:

int anUnsignedByte = (int) aSignedByte & 0xff;
Peter Knego
fuente
¿Es necesario el reparto a un int?
Nich
Puede ser un elenco implícito, pero hay un elenco de cualquier manera. Y ese elenco hace extensión firmada. Y eso es un problema. Si realiza un reparto explícito, al menos puede ver que esto está sucediendo.
foo
4

Una nota al margen, si desea imprimirlo, simplemente puede decir

byte b = 255;
System.out.println((b < 0 ? 256 + b : b));
Kyle Kinkade
fuente
66
¿Por qué tan complejo? println(b & 0xff)es suficiente
phuclv
0

Si crees que estás buscando algo como esto.

public static char toUnsigned(byte b) {
    return (char) (b >= 0 ? b : 256 + b);
}
Tobias Johansson
fuente
0

Adamski proporcionó la mejor respuesta, pero no está del todo completa, así que lea su respuesta, ya que explica los detalles que no soy.

Si tiene una función del sistema que requiere que se le pase un byte sin signo, puede pasar un byte con signo, ya que lo tratará automáticamente como un byte sin signo.

Entonces, si una función del sistema requiere cuatro bytes, por ejemplo, 192 168 0 1 como bytes sin signo, puede pasar -64 -88 0 1, y la función seguirá funcionando, porque el acto de pasarlos a la función los anulará. .

Sin embargo, es poco probable que tenga este problema ya que las funciones del sistema están ocultas detrás de las clases para la compatibilidad multiplataforma, aunque algunos de los métodos de lectura java.io devuelven bytes no seleccionados como int.

Si desea que esto funcione, intente escribir bytes firmados en un archivo y vuelva a leerlos como bytes sin firmar.

Oveja Loon
fuente
1
No existen los bytes con signo o sin signo.
Vlastimil Ovčáčík
¿Cómo exactamente estabas escribiendo y leyendo los bytes en tu ejemplo?
Vlastimil Ovčáčík
0

Tú también puedes:

public static int unsignedToBytes(byte a)
{
    return (int) ( ( a << 24) >>> 24);
}    

Explicación:

digamos a = (byte) 133;

En la memoria se almacena como: "1000 0101" (0x85 en hexadecimal)

Por lo que su representación se traduce sin signo = 133, firmado = -123 (como complemento a 2)

a << 24

Cuando el desplazamiento a la izquierda se realiza 24 bits a la izquierda, el resultado ahora es un entero de 4 bytes que se representa como:

"10000101 00000000 00000000 00000000" (o "0x85000000" en hexadecimal)

entonces nosotros tenemos

(a << 24) >>> 24

y se desplaza nuevamente a la derecha 24 bits pero se llena con ceros a la izquierda. Entonces resulta a:

"00000000 00000000 00000000 10000101" (o "0x00000085" en hexadecimal)

y esa es la representación sin signo que equivale a 133.

Si trató de emitir a = (int) a; , lo que sucedería es que mantiene la representación del byte del complemento de 2 y lo almacena como int también como complemento de 2:

(int) "10000101" ---> "11111111 11111111 11111111 10000101"

Y eso se traduce como: -123

mark_infinite
fuente
2
En 2019, esto es innecesario. Solo úsalo java.lang.Byte.toUnsignedInt(byte value). Y si aún no está utilizando Java 8, actualice lo antes posible. Java 7 y versiones anteriores son el final de la vida.
Stephen C
0

Estoy tratando de usar estos datos como parámetro para una función de Java que acepta solo un byte como parámetro

Esto no es sustancialmente diferente de una función que acepta un número entero al que desea pasar un valor mayor que 2 ^ 32-1.

Parece que depende de cómo se defina y documente la función; Puedo ver tres posibilidades:

  1. Puede documentar explícitamente que la función trata el byte como un valor sin signo, en cuyo caso la función probablemente debería hacer lo que espera pero parece implementarse incorrectamente. Para el caso de enteros, la función probablemente declararía el parámetro como un entero sin signo, pero eso no es posible para el caso de bytes.

  2. Puede documentar que el valor de este argumento debe ser mayor que (o tal vez igual a) cero, en cuyo caso está haciendo un mal uso de la función (pasando un parámetro fuera de rango), esperando que haga más de lo que fue diseñado para hacer. Con cierto nivel de soporte de depuración, puede esperar que la función arroje una excepción o falle una aserción.

  3. La documentación puede no decir nada, en cuyo caso un parámetro negativo es, bueno, un parámetro negativo y si eso tiene algún significado depende de lo que haga la función. Si esto no tiene sentido, entonces tal vez la función realmente debería definirse / documentarse como (2). Si esto es significativo de una manera no obvia (p. Ej., Los valores no negativos se usan para indexar en una matriz, y los valores negativos se usan para indexar desde el final de la matriz, entonces -1 significa el último elemento) la documentación debe decir qué significa y esperaría que no sea lo que quieres que haga de todos modos.

Kevin Martin
fuente
Hmmm, creo que acabo de publicar una respuesta destinada a otra pregunta sobre la firma de los bytes, pero supongo que todavía es un poco relevante aquí también ...
Kevin Martin
-1

Si tiene una función que debe pasar un byte firmado, ¿qué espera que haga si pasa un byte sin firmar?

¿Por qué no puedes usar ningún otro tipo de datos?

Por lo general, puede usar un byte como byte sin firmar con traducciones simples o sin traducciones. Todo depende de cómo se use. Tendrás que aclarar qué tienes que hacer con él.

Peter Lawrey
fuente
-1

Aunque puede parecer molesto (proveniente de C) que Java no incluyó el byte sin signo en el lenguaje, realmente no es gran cosa ya que una simple operación "b & 0xFF" produce el valor sin signo para el byte b (firmado) en el (raro) situaciones que realmente se necesitan. Los bits en realidad no cambian, solo la interpretación (que es importante solo cuando se realizan, por ejemplo, algunas operaciones matemáticas en los valores).

Beto
fuente
mira la respuesta de otros, ¿crees que tu respuesta es la mejor / útil? describir en poco y agregarlo en los comentarios
Jubin Patel
8
No es raro solo porque no lo has encontrado. Intente implementar un protocolo y lo encontrará un millón de veces. Lo molesto es que la gran mayoría de los casos de uso que he encontrado que tratan con bytes, quieres tratar con bytes sin firmar (porque son bytes, no números). Lo loco es que CUALQUIER operación bit a bit lo convertirá en int, lo que significa que cualquier valor "negativo" será completamente diferente cuando se extienda. Sí, puede evitarlo enmascarando siempre, pero es una pérdida de tiempo, un procesador y causa errores realmente oscuros si lo olvida.
Thor84no
Estoy de acuerdo con Thor84no: los bytes no son números y no deberían tener signo. Por otro lado, dado que no son números, ni siquiera deberíamos tener / usar los operadores + y -. Usar solo operadores bit a bit funciona bien, por otro lado, los operadores de desplazamiento no funcionan como a uno le gustaría, y de hecho, java promueve un byte desplazado a un int.
user1708042
1
@ VlastimilOvčáčík Eso es literalmente imposible en este caso, eso es lo más inquietante. Usted repite en x & 0xFFtodas partes donde lo necesite o repite algo como en behaveLikeAnUnsignedByte(x)todas partes. Esto es necesario para cada lugar donde usa un valor de byte o una matriz de bytes que no debe estar firmada, no hay una forma concebible de evitar esta repetición. No puede escribir la implementación de un protocolo que lee y escribe valores de bytes con una sola referencia a una variable de bytes. Sin embargo, su visión simplista podría explicar por qué nunca se preocuparon por solucionarlo.
Thor84no
-1

No hay un byte sin signo en Java, pero si desea mostrar un byte, puede hacer,

int myInt = 144;

byte myByte = (byte) myInt;

char myChar = (char) (myByte & 0xFF);

System.out.println("myChar :" + Integer.toHexString(myChar));

Salida:

myChar : 90

Para obtener más información, consulte Cómo mostrar un valor hexadecimal / byte en Java .

Jyo the Whiff
fuente
No hay necesidad de definir esto usted mismo. java.lang.Byte.toUnsignedInt(byte value);existe para esto.
Alexander - Restablece a Mónica el
-2

Según las limitaciones en Java, el byte sin signo es casi imposible en el formato de tipo de datos actual. Puede buscar otras bibliotecas de otro idioma para lo que está implementando y luego puede llamarlas utilizando JNI .

Pritesh Jain
fuente
No creo que quiera almacenarlo como un byte firmado. Lo está recibiendo como un byte firmado y quiere almacenarlo como un int, que es perfectamente válido. Su problema es que donde sea que esté recibiendo información representa un valor entre 0 y 255 como un byte, pero Java lo interpreta como un valor con signo complementario de dos porque java no admite bytes con signo.
Zac
-2

Si y no. He estado cavando con este problema. Como si entendiera esto:

El hecho es que Java ha firmado el número entero -128 a 127 .. Es posible presentar un Java no firmado con:

public static int toUnsignedInt(byte x) {
    return ((int) x) & 0xff;
}

Si, por ejemplo, agrega -12 número firmado para que no esté firmado, obtiene 244. Pero puede usar ese número nuevamente en firmado, debe volver a firmar y volverá a ser -12.

Si intenta agregar 244 al byte de Java, saldrá de la excepción de índice de excepción.

Salud..

Sindri Þór
fuente
3
No hay necesidad de definir esto usted mismo. java.lang.Byte.toUnsignedInt(byte value);existe para esto.
Alexander - Restablece a Mónica el
-3

Si desea bytes sin firmar en Java, simplemente reste 256 del número que le interesa. Producirá el complemento de dos con un valor negativo, que es el número deseado en bytes sin signo.

Ejemplo:

int speed = 255; //Integer with the desired byte value
byte speed_unsigned = (byte)(speed-256);
//This will be represented in two's complement so its binary value will be 1111 1111
//which is the unsigned byte we desire.

Debe usar estos trucos sucios cuando use leJOS para programar el ladrillo NXT .

XapaJIaMnu
fuente
Te das cuenta de que el valor binario de 255 también es 1111 1111, por lo que no es necesario restar, ¿verdad?
Nick White
@NickWhite, sí en binario. Pero Java usa el comentario de 2 donde 255 no es 11111111
XapaJIaMnu
Lo siento, pero esto está mal. Intenta algunos experimentos. El valor en speed_unsignedestá firmado. Imprímelo y mira. (Y - 256no logra nada aquí.)
Stephen C