Comportamento estranho quando Java converte int em byte?

130
int i =132;

byte b =(byte)i; System.out.println(b);

Incompreensível. Por que a saída -124?

maldade
fonte

Respostas:

172

Em Java, um inté de 32 bits. A byteé 8 bits.

A maioria dos tipos primitivos em Java são assinados, e byte, short, int, e longsão codificados em complemento de dois. (O chartipo não está assinado e o conceito de sinal não é aplicável boolean.)

Nesse esquema numérico, o bit mais significativo especifica o sinal do número. Se forem necessários mais bits, o bit mais significativo ("MSB") é simplesmente copiado para o novo MSB.

Portanto, se você possui byte 255: 11111111 e deseja representá-lo como um int(32 bits), basta copiar o 1 para a esquerda 24 vezes.

Agora, uma maneira de ler o número de complemento negativo de dois é começar com o bit menos significativo, mover para a esquerda até encontrar o primeiro 1 e depois inverter todos os bits depois. O número resultante é a versão positiva desse número

Por exemplo: 11111111vai para 00000001= -1. É isso que Java exibirá como o valor.

O que você provavelmente deseja fazer é conhecer o valor não assinado do byte.

Você pode fazer isso com uma máscara de bit que exclui tudo, menos os 8 bits menos significativos. (0xff)

Assim:

byte signedByte = -1;
int unsignedByte = signedByte & (0xff);

System.out.println("Signed: " + signedByte + " Unsigned: " + unsignedByte);

Seria impresso: "Signed: -1 Unsigned: 255"

O que realmente está acontecendo aqui?

Estamos usando AND bit a bit para mascarar todos os bits de sinal estranhos (os 1 à esquerda dos 8 bits menos significativos). Quando um int é convertido em um byte, o Java retira os 24 bits mais à esquerda

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

Como o 32º bit agora é o sinal em vez do 8º (e definimos o sinal como 0, o que é positivo), os 8 bits originais do byte são lidos por Java como um valor positivo.

Wayne
fonte
1
bem feito, a melhor explicação sobre esse assunto, Wayne! Estou apenas procurando pela formalização matemática porque na representação do complemento de dois o bit de sinal pode ser copiado à direita para adicionar bits. É fácil entendê-lo pensando na regra de como obter o número negativo. isto é: considere todos os bits da direita para a esquerda e escreva-os inalterados até o primeiro 1 compreendido. Inverta os bits subsequentes. Se eu considerar o bit que falta ser 0, é fácil entender que todos eles vão para 1. Mas eu estava procurando uma explicação mais 'matemática'.
AgostinoX
O signedByte & (0xff)que 0xffestá acontecendo aqui é que é um literal de intergeração, assim, o namedByte é promovido a um número inteiro antes da operação bit a bit ser executada.
Kevin Wheeler
Isso não é 0xFF, é 0x7E no seu exemplo!
21818 JohnyTex 11/11
89

132em dígitos ( base 10 ) está 1000_0100em bits ( base 2 ) e Java armazena intem 32 bits:

0000_0000_0000_0000_0000_0000_1000_0100

O algoritmo para int-to-byte é truncado à esquerda; O algoritmo for System.out.printlné o complemento de dois (o complemento de dois é se o bit mais à esquerda for 1, interprete como complemento de um negativo (bits invertidos) menos um.); Assim System.out.println(int-to-byte( ))é:

  • interpretar como (se o bit mais à esquerda for 1) [negativo (bits invertidos (menos um (] truncado à esquerda ( 0000_0000_0000_0000_0000_0000_1000_0100) [)))])
  • = interpretar como (se o bit mais à esquerda for 1) [negativo (bits invertidos (menos um (] 1000_0100[)))])
  • = interpretar como (negativo (bits invertidos (menos um ( 1000_0100))))
  • = interpretar como (negativo (bits invertidos ( 1000_0011)))
  • = interpretar como (negativo ( 0111_1100))
  • = interpretar como (negativo (124))
  • = interpretar como (-124)
  • = -124 Tada !!!
Pacerier
fonte
7
Muito bem explicado
ZAJ 9/16
1
Então agora 132 em decimal é -124 em byte. Como funciona o inverso?
Nilesh Deokar
@NileshDeokar, O contrário é da POLA, uma vez que eles se encaixam (; cf JLS 5.1.2 ); a saída coincide com o sinal esquerdo do teclado ( 0para positivo e 1negativo).
Pacerier 28/05/19
O que é POLA? A conversão de intpara a byteé uma conversão com perdas (isto é, informações são perdidas). Portanto, não há como convertê-lo novamente em seu intvalor original .
truthadjustr
23

O byte em Java é assinado, portanto, possui um intervalo de -2 ^ 7 a 2 ^ 7-1 - ou seja, -128 a 127. Como 132 está acima de 127, você termina com 132-256 = -124. Ou seja, essencialmente 256 (2 ^ 8) são adicionados ou subtraídos até que caiam no intervalo.

Para mais informações, você pode ler sobre o complemento de dois .

bdonlan
fonte
16

132 está fora do intervalo de um byte que é de -128 a 127 (Byte.MIN_VALUE a Byte.MAX_VALUE) Em vez disso, o bit superior do valor de 8 bits é tratado como o assinado, o que indica que é negativo nesse caso. Portanto, o número é 132 - 256 = -124.

Peter Lawrey
fonte
5

aqui está um método muito mecânico sem as teorias que distraem:

  1. Converta o número em representação binária (use uma calculadora ok?)
  2. Copie apenas os 8 bits mais à direita (LSB) e descarte o restante.
  3. No resultado da etapa 2, se o bit mais à esquerda for 0, use uma calculadora para converter o número em decimal. Esta é a sua resposta.
  4. Senão (se o bit mais à esquerda for 1), sua resposta será negativa. Deixe todos os zeros mais à direita e o primeiro bit diferente de zero inalterado. E inverteu o resto, ou seja, substitua 1 por 0 e 0 por 1. Em seguida, use uma calculadora para converter para decimal e acrescente um sinal negativo para indicar que o valor é negativo.

Este método mais prático está de acordo com as muitas respostas teóricas acima. Portanto, aqueles que ainda estão lendo aqueles livros em Java que dizem usar o módulo, isso está definitivamente errado, pois as 4 etapas descritas acima definitivamente não são uma operação do módulo.

truthadjustr
fonte
Quais livros em Java estão dizendo para usar 'modulo'? Eu nunca vi nenhum livro de CS afirmando isso em 46 anos, muito menos qualquer livro de Java. O que 'módulo'? Não há operação de módulo em Java. Apenas um operador restante.
Marquês de Lorne
grep mais difícil. http://iiti.ac.in/people/~tanimad/JavaTheCompleteReference.pdfpage 59
truthadjustr
4

Equação do complemento de dois:

insira a descrição da imagem aqui


Em Java, byte(N = 8) e int(N = 32) são representados pelo complemento 2s mostrado acima.

A partir da equação, um 7 é negativo para, bytemas positivo para int.

coef:   a7    a6  a5  a4  a3  a2  a1  a0
Binary: 1     0   0   0   0   1   0   0
----------------------------------------------
int:    128 + 0 + 0 + 0 + 0 + 4 + 0 + 0 =  132
byte:  -128 + 0 + 0 + 0 + 0 + 4 + 0 + 0 = -124
bcorso
fonte
2

geralmente nos livros, você encontra a explicação da conversão de int para byte como sendo executada pela divisão do módulo. isso não está estritamente correto, como mostrado abaixo, o que realmente acontece é que os 24 bits mais significativos do valor binário do número int são descartados, deixando confusão se o bit restante à esquerda estiver definido, que designa o número como negativo

public class castingsample{

public static void main(String args[]){

    int i;
    byte y;
    i = 1024;
    for(i = 1024; i > 0; i-- ){

      y = (byte)i;
      System.out.print(i + " mod 128 = " + i%128 + " also ");
      System.out.println(i + " cast to byte " + " = " + y);

    }

}

}
caneta
fonte
2
Eu nunca vi isso em nenhum livro em 46 anos.
Marquês de Lorne
2

Um algoritmo rápido que simula a maneira como funciona é o seguinte:

public int toByte(int number) {
    int tmp = number & 0xff
    return (tmp & 0x80) == 0 ? tmp : tmp - 256;
}

Como isso funciona? Olhe para a resposta daixtr . Uma implementação do algoritmo exato descrito em sua resposta é a seguinte:

public static int toByte(int number) {
    int tmp = number & 0xff;
    if ((tmp & 0x80) == 0x80) {
        int bit = 1;
        int mask = 0;
        for(;;) {
            mask |= bit;
            if ((tmp & bit) == 0) {
                bit <<=1;
                continue;
            }
            int left = tmp & (~mask);
            int right = tmp & mask;
            left = ~left;
            left &= (~mask);
            tmp = left | right;
            tmp = -(tmp & 0xff);
            break;
        }
    }
    return tmp;
}
Francisco Neto
fonte
1

Se você quiser entender isso matematicamente, como isso funciona

portanto, basicamente, os números b / w -128 a 127 serão escritos da mesma forma que seu valor decimal, acima do seu (seu número - 256).

por exemplo. 132, a resposta será 132 - 256 = - 124 ie

256 + sua resposta no número 256 + (-124) é 132

Outro exemplo

double a = 295.04;
int b = 300;
byte c = (byte) a;
byte d = (byte) b; System.out.println(c + " " + d);

a saída será 39 44

(295 - 256) (300 - 256)

NOTA: não considerará números após o decimal.

Prakhar Lohiya
fonte
0

Conceitualmente, subtrações repetidas de 256 são feitas no seu número, até que ele esteja no intervalo de -128 a +127. Portanto, no seu caso, você começa com 132 e termina com -124 em uma etapa.

Computacionalmente, isso corresponde à extração dos 8 bits menos significativos do seu número original. (E observe que o bit mais significativo desses 8 se torna o bit de sinal.)

Observe que em outros idiomas esse comportamento não está definido (por exemplo, C e C ++).

Bathsheba
fonte
Para ser claro, o resultado obtido é o mesmo que se fossem feitas subtrações repetidas. Na prática, a JVM não faz isso dessa maneira. (Seria terrivelmente ineficiente!)
Stephen C
De fato. Espero que meu segundo parágrafo cubra como a JVM realmente faz isso. Mas eu brinquei um pouco com a minha língua.
Bathsheba
1
Sim. A mudança de "essencialmente" para "conceitualmente" faz uma enorme diferença!
Stephen C
-1
 N is input number
case 1: 0<=N<=127  answer=N;
case 2: 128<=N<=256 answer=N-256 
case 3: N>256   
        temp1=N/256;
        temp2=N-temp*256;
        if temp2<=127   then answer=temp2;
        else if temp2>=128  then answer=temp2-256;
case 4: negative  number input
        do same procedure.just change the sign of the solution           
devil_29
fonte
A resposta correta é alcançada através de mascaramento de bits, não por divisão e restante.
Marquês de Lorne