Podemos criar byte não assinado em Java

185

Estou tentando converter um byte assinado em não assinado. O problema é que os dados que estou recebendo não são assinados e o Java não suporta byte não assinado; portanto, quando lê os dados, trata-os como assinados.

Tentei convertê-lo pela seguinte solução que obtive do Stack Overflow.

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

Mas quando é convertido novamente em byte, recebo os mesmos dados assinados. Estou tentando usar esses dados como um parâmetro para uma função de Java que aceita apenas um byte como parâmetro, portanto, não posso usar nenhum outro tipo de dados. Como posso resolver este problema?

dln
fonte
2
Guava: UnsignedBytes.toint (valor de byte)
jacktrades
20
java.lang.Byte.toUnsignedInt (valor do byte);
themarketka

Respostas:

107

Não sei se entendi sua pergunta.

Eu apenas tentei isso e para o byte -12 (valor assinado) ele retornou o número inteiro 244 (equivalente ao valor do byte não assinado, mas digitado como um int):

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

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

É o que você quer fazer?

Java não permite expressar 244 como um bytevalor, como seria C. Para expressar números inteiros positivos acima Byte.MAX_VALUE(127), você deve usar outro tipo de número inteiro, como short, intou long.

Guillaume
fonte
1
byte b = (byte)unsignedToBytes((byte) -12); Agora tente imprimir b
Jigar Joshi
101
Por que você aceitou isso como a resposta correta? Tudo o que faz é exatamente o mesmo que o método mencionado na sua pergunta - converta um byte em um número inteiro não assinado.
Adamski
1
É importante, às vezes, ter valores assinados, outras não assinados; provavelmente, esse é o motivo pelo qual ele aceitou esta resposta. (byte) (b & 0xff) não tem nenhum sentido, mas (byte) (Math.min ((b & 0xff) * 2, 255)) tem sentido; por exemplo, em computação gráfica, apenas fará o pix representado por o byte duas vezes mais brilhante. :-)
iirekm
3
Poderia ser chamado byteToUnsigned também
Hernán Eche
195

O fato de as primitivas serem assinadas em Java é irrelevante para como elas são representadas na memória / trânsito - um byte é de apenas 8 bits e se você interpreta isso como um intervalo assinado ou não, é com você. Não há sinalizador mágico para dizer "isto está assinado" ou "isto não está assinado".

Como as primitivas são assinadas, o compilador Java impedirá que você atribua um valor maior que +127 a um byte (ou menor que -128). No entanto, não há nada para impedir que você faça downcast de um int (ou curto) para conseguir isso:

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
fonte
5
Para muitas operações, não faz diferença, no entanto, para algumas operações, faz. De qualquer maneira, você pode usar um byte como não assinado ou char, que não está assinado.
Peter Lawrey
62
Acessar uma matriz com um número potencialmente negativo não é irrelevante.
31412 Stefan
3
@ Stefan - eu quis dizer irrelevante no contexto de como eles são representados no fio.
11333 Adamski
6
O que é um tanto irrelevante para a questão. Como ele mencionou que precisa passá-lo para uma função que aceita apenas parâmetros de bytes, não importa o clima, nós a interpretamos como a representação de bytes de um unicórnio. O Java sempre o tratará como um número assinado, o que pode ser problemático para um exemplo quando essa função usa o parâmetro como um índice. No entanto, para ser justo, eu também diminuí a votação para as outras 2 principais respostas, pois elas também não respondem à pergunta.
Stefan
2
@ Stefan +1 para você. Absolutamente relevante se você estiver usando o byte para acessar uma matriz de 256 elementos. Isso é um excelente exemplo para demonstrar por que todos devem começar a aprender C e C ++ antes de passar para Java ou C #
Gianluca Ghettini
46

Guia completo para trabalhar com bytes não assinados em Java:

Byte não assinado em Java

(Fonte para esta resposta.)


A linguagem Java não fornece nada parecido com a unsignedpalavra - chave. A de byteacordo com a especificação da linguagem representa um valor entre -128 - 127. Por exemplo, se a bytefor convertido para um intJava, ele interpretará o primeiro bit como sinal e usará a extensão de sinal .

Dito isto, nada impede que você veja byteapenas 8 bits e interprete esses bits como um valor entre 0 e 255. Lembre-se de que não há nada que você possa fazer para forçar sua interpretação sobre o método de outra pessoa. Se um método aceitar umbyte , esse método aceita um valor entre -128 e 127, a menos que seja explicitamente indicado o contrário.

Aqui estão algumas conversões / manipulações úteis para sua conveniência:

Conversões de / para 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

(Ou, se você estiver no Java 8 ou superior, use Byte.toUnsignedInt .)

Análise / formatação

A melhor maneira é usar as conversões acima:

// 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

A representação de 2 complementos "simplesmente funciona" para adição, subtração e multiplicação:

// 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

A divisão requer conversão manual de operandos:

byte ratio = (byte) ((b1 & 0xFF) / (b2 & 0xFF));
aioobe
fonte
1
'char' não representa um número.
logoff
26
Para resumir: você está errado .
Aioobe 27/09/12
36

Não há bytes não assinados primitivos em Java. O normal é convertê-lo para um tipo maior:

int anUnsignedByte = (int) aSignedByte & 0xff;
Peter Knego
fonte
O elenco para um int é necessário?
nich
Pode ser um elenco implícito, mas existe um elenco de qualquer maneira. E esse elenco faz extensão assinada. E isso é um problema. Se você faz uma transmissão explícita, pode pelo menos ver que isso está acontecendo.
Foo
4

Uma nota lateral, se você quiser imprimi-lo, basta dizer

byte b = 255;
System.out.println((b < 0 ? 256 + b : b));
Kyle Kinkade
fonte
6
por que tão complexo? println(b & 0xff)é suficiente
phuclv 26/11/16
0

Se acha que está procurando algo assim.

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

Adamski forneceu a melhor resposta, mas ela não está completa, então leia sua resposta, pois explica os detalhes que não estou.

Se você possui uma função do sistema que requer que um byte não assinado seja passado para ele, você pode transmitir um byte assinado, pois ele o tratará automaticamente como um byte não assinado.

Portanto, se uma função do sistema exigir quatro bytes, por exemplo, 192 168 0 1 como bytes não assinados, você poderá passar -64 -88 0 1, e a função ainda funcionará, porque o ato de passá-los para a função os cancelará a assinatura .

No entanto, é improvável que você tenha esse problema, pois as funções do sistema estão ocultas atrás das classes para compatibilidade entre plataformas, embora alguns dos métodos de leitura java.io retornem bytes não suspirados como int.

Se você quiser ver isso funcionando, tente escrever bytes assinados em um arquivo e leia-os novamente como bytes não assinados.

Ovelha Loon
fonte
1
Não existe bytes assinados ou não assinados.
Vlastimil Ovčáčík 15/08/2015
Como exatamente você estava escrevendo e lendo os bytes em seu exemplo?
Vlastimil Ovčáčík 15/08/2015
0

Você também pode:

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

Explicação:

Digamos a = (byte) 133;

Na memória, é armazenado como: "1000 0101" (0x85 em hex)

Portanto, sua representação traduz não assinado = 133, assinado = -123 (como complemento de 2)

a << 24

Quando o deslocamento para a esquerda é executado 24 bits para a esquerda, o resultado é agora um número inteiro de 4 bytes, representado como:

"10000101 00000000 00000000 00000000" (ou "0x85000000" em hexadecimal)

então nós temos

(a << 24) >>> 24

e muda novamente à direita 24 bits, mas é preenchido com zeros à esquerda. Portanto, resulta em:

"00000000 00000000 00000000 10000101" (ou "0x00000085" em hexadecimal)

e essa é a representação não assinada que é igual a 133.

Se você tentou transmitir a = (int) a; , o que aconteceria é manter a representação do byte do complemento 2 e armazená-lo como int também como o complemento do 2:

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

E isso se traduz como: -123

mark_infinite
fonte
2
Em 2019, isso é desnecessário. Apenas use java.lang.Byte.toUnsignedInt(byte value). E se você ainda não estiver usando o Java 8, atualize o mais rápido possível. O Java 7 e versões anteriores estão em fim de vida.
Stephen C
0

Estou tentando usar esses dados como parâmetro para uma função do Java que aceita apenas um byte como parâmetro

Isso não é substancialmente diferente de uma função que aceita um número inteiro para o qual você deseja passar um valor maior que 2 ^ 32-1.

Parece que depende de como a função é definida e documentada; Eu posso ver três possibilidades:

  1. Pode documentar explicitamente que a função trata o byte como um valor não assinado; nesse caso, a função provavelmente deve fazer o que você espera, mas parece estar implementada incorretamente. Para o caso inteiro, a função provavelmente declararia o parâmetro como um inteiro não assinado, mas isso não é possível para o caso de byte.

  2. Ele pode documentar que o valor desse argumento deve ser maior que (ou talvez igual a) zero; nesse caso, você está fazendo mau uso da função (passando um parâmetro fora do intervalo), esperando que faça mais do que foi projetado para Faz. Com algum nível de suporte à depuração, você pode esperar que a função gere uma exceção ou falhe em uma asserção.

  3. A documentação pode não dizer nada; nesse caso, um parâmetro negativo é, bem, um parâmetro negativo e se isso tem algum significado depende do que a função faz. Se isso não faz sentido, talvez a função deva realmente ser definida / documentada como (2). Se isso for significativo de uma maneira não óbvia (por exemplo, valores não negativos são usados ​​para indexar em uma matriz, e valores negativos são usados ​​para indexar de volta a partir do final da matriz, então -1 significa o último elemento) a documentação deve dizer o que significa e eu esperaria que não é o que você quer fazer de qualquer maneira.

Kevin Martin
fonte
Hummm, acho que acabei de postar uma resposta destinada a outra pergunta sobre a assinatura de bytes, mas suponho que ainda seja um pouco relevante aqui também ...
Kevin Martin
-1

Se você tem uma função que deve receber um byte assinado, o que você espera que faça se passar um byte não assinado?

Por que você não pode usar outro tipo de dados?

Desnecessariamente, você pode usar um byte como um byte não assinado com traduções simples ou sem traduções. Tudo depende de como é usado. Você precisaria esclarecer o que pretende fazer com isso.

Peter Lawrey
fonte
-1

Embora possa parecer irritante (vindo de C) que Java não tenha incluído byte não assinado na linguagem, isso não é grande coisa, pois uma operação simples "b & 0xFF" gera o valor não assinado para o byte (assinado) b no (raro) situações em que é realmente necessário. Na verdade, os bits não mudam - apenas a interpretação (que é importante apenas ao realizar, por exemplo, algumas operações matemáticas nos valores).

Prumo
fonte
procure resposta de outros, você acha que sua resposta é melhor / útil? descrever em pouco e adicioná-lo nos comentários
Jubin Patel
8
Não é raro apenas porque você não se deparou com isso. Tente implementar um protocolo e você o encontrará um milhão de vezes. O irritante é que, na grande maioria dos casos de uso que eu lidei com bytes, você deseja lidar com bytes não assinados (porque são bytes, não números). O louco é que QUALQUER operação bit a bit a converterá em um int, o que significa que quaisquer valores "negativos" serão valores completamente diferentes quando estendidos. Sim, você pode contornar isso mascarando sempre, mas é uma perda de tempo, processador e causa bugs realmente obscuros, se você esquecer.
Thor84no
Concordo com Thor84no: bytes não são números e não devem ter sinal. Por outro lado, como não são números, não devemos nem ter / usar os operadores + e -. Usar apenas operadores bit a bit funciona bem, por outro lado, os operadores de turno não funcionam como se gostaria e, de fato, o java promove um byte deslocado para um int.
precisa saber é o seguinte
1
@ VlastimilOvčáčík Isso é literalmente impossível neste caso, essa é a coisa agitante. Você repete em x & 0xFFqualquer lugar que precisar ou repete algo como em behaveLikeAnUnsignedByte(x)qualquer lugar. Isso é necessário para cada local em que você usa um valor de bytes ou uma matriz de bytes que precisa ser sem sinal; não há maneira concebível de evitar essa repetição. Você não pode escrever a implementação de um protocolo que lê e grava valores de bytes com apenas uma única referência a uma variável de bytes. Sua visão simplista pode explicar por que eles nunca se importaram em corrigi-lo.
precisa saber é o seguinte
-1

Não há byte não assinado em Java, mas se você deseja exibir um byte, pode fazê-lo,

int myInt = 144;

byte myByte = (byte) myInt;

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

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

Resultado:

myChar : 90

Para obter mais informações, consulte Como exibir um valor hex / byte em Java .

Jyo, o Whiff
fonte
Não há necessidade de definir isso sozinho. java.lang.Byte.toUnsignedInt(byte value);existe para isso.
Alexander - Restabelece Monica
-2

Conforme as limitações em Java, o byte não assinado é quase impossível no formato de tipo de dados atual. Você pode procurar outras bibliotecas de outro idioma para o que está implementando e, em seguida, chamá-las usando JNI .

Pritesh Jain
fonte
Não acho que ele queira armazená-lo como um byte assinado. Ele está recebendo como um byte assinado e deseja armazená-lo como um int, o que é perfeitamente válido. Seu problema é que, de onde quer que ele esteja recebendo, está representando um valor entre 0 e 255 como um byte, mas o Java interpreta isso como dois complementando o valor assinado, porque o java não suporta bytes assinados.
Zac
-2

Sim e não. Eu tenho procurado por esse problema. Como eu entendo isso:

O fato é que java assinou o interger -128 a 127. É possível apresentar um não assinado em java com:

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

Se, por exemplo, você adicionar um número -12 assinado para não assinar, obtém 244. Mas você pode usar esse número novamente em um sinal assinado, ele deve ser alterado novamente para sinalizado e será novamente -12.

Se você tentar adicionar 244 ao java byte, obterá outOfIndexException.

Felicidades..

Sindri Þór
fonte
3
Não há necessidade de definir isso sozinho. java.lang.Byte.toUnsignedInt(byte value);existe para isso.
Alexander - Restabelece Monica
-3

Se você deseja bytes não assinados em Java, basta subtrair 256 do número em que está interessado. Isso produzirá o complemento de dois com um valor negativo, que é o número desejado em bytes não assinados.

Exemplo:

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.

Você precisa usar hacks sujos ao usar o leJOS para programar o bloco NXT .

XapaJIaMnu
fonte
Você percebe que o valor binário de 255 também é 1111 1111; portanto, nenhuma subtração é necessária, certo?
22615 Nick White:
@ NickWhite, sim em binário. Mas comlement Java da usos 2 de onde 255 não é 11111111
XapaJIaMnu
Desculpe, mas isso está errado. Tente algumas experiências. O valor em speed_unsignedé assinado. Imprima e veja. (E o que - 256não alcança nada aqui.)
Stephen C