Comportamento strano quando Java converte int in byte?


130
int i =132;

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

Mindboggling. Perché l'output -124?

Risposte:


172

In Java, un intè 32 bit. A byteè 8 bits.

La maggior parte dei tipi primitivi in Java sono firmati, e byte, short, int, e longsono codificati in complemento a due. (Il chartipo non è firmato e il concetto di segno non è applicabile boolean.)

In questo schema numerico il bit più significativo specifica il segno del numero. Se sono necessari più bit, il bit più significativo ("MSB") viene semplicemente copiato nel nuovo MSB.

Quindi se hai byte 255: 11111111 e vuoi rappresentarlo come int(32 bit) devi semplicemente copiare 1 a sinistra 24 volte.

Ora, un modo per leggere un numero di complemento di due negativi è iniziare con il bit meno significativo, spostarsi a sinistra fino a trovare il primo 1, quindi invertire ogni bit dopo. Il numero risultante è la versione positiva di quel numero

Ad esempio: 11111111va a 00000001= -1. Questo è ciò che Java mostrerà come valore.

Quello che probabilmente vuoi fare è conoscere il valore senza segno del byte.

Puoi farlo con una maschera di bit che elimina tutto tranne gli 8 bit meno significativi. (0xFF)

Così:

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

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

Stampa: "Signed: -1 Unsigned: 255"

Cosa sta realmente succedendo qui?

Stiamo usando bit a bit AND per mascherare tutti i bit di segno estranei (gli 1 a sinistra degli 8 bit meno significativi). Quando un int viene convertito in un byte, Java taglia via i 24 bit più a sinistra

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

Poiché il 32 ° bit è ora il bit di segno anziché l'ottavo bit (e impostiamo il bit di segno su 0 che è positivo), gli 8 bit originali del byte vengono letti da Java come valore positivo.


1
ben fatto, la migliore spiegazione su questo argomento, Wayne! Sto solo cercando la formalizzazione matematica perché nella rappresentazione del complemento a due il bit del segno può essere copiato sulla destra per aggiungere bit. È facile capirlo pensando alla regola di come ottenere il negativo di un numero. cioè: considera tutti i bit da destra a sinistra e scrivili invariati fino al primo 1 compreso. Quindi invertire i bit successivi. Se considero il bit mancante come 0, è facile capire che vanno tutti a 1. Ma stavo cercando una spiegazione più "matematica".
AgostinoX,

Ciò signedByte & (0xff)che 0xffsta affermando qui è che è un intero letterale, quindi signedByte viene promosso a un numero intero prima che venga eseguita l'operazione bit a bit.
Kevin Wheeler,

Non è 0xFF, è 0x7E nel tuo esempio!
JohnyTex,

89

132in cifre ( base 10 ) è 1000_0100in bit ( base 2 ) e Java memorizza intin 32 bit:

0000_0000_0000_0000_0000_0000_1000_0100

L'algoritmo per int-to-byte è troncato a sinistra; L'algoritmo per System.out.printlnè il complemento a due (il complemento a due è se il bit più a sinistra è 1, interpretare come complemento a uno negativo (bit invertiti) meno uno.); Quindi System.out.println(int-to-byte( ))è:

  • interpret-as (if-leftmost-bit-is-1 [negativo (invert-bit (meno-one (] left-truncate ( 0000_0000_0000_0000_0000_0000_1000_0100) [)))])
  • = interpret-as (if -mostmost-bit-is-1 [negative (invert-bit (meno-one (] 1000_0100[)))])
  • = interpret-as (negativo (bit di inversione (meno uno ( 1000_0100))))
  • = interpret-as (negativo (invert-bit ( 1000_0011)))
  • = interpretare come (negativo ( 0111_1100))
  • = Interpret-as (negativo (124))
  • = Interpret-as (-124)
  • = -124 Tada !!!

7
Molto ben spiegato
ZAJ

1
Quindi ora 132 in decimale è -124 in byte. Come funziona il contrario?
Nilesh Deokar

@NileshDeokar, Il contrario è di POLA poiché si adattano (; cfr. JLS 5.1.2 ); l'output coincide con sign-leftpad ( 0per positivo e 1per negativo).
Pacerier,

Che cos'è POLA? La conversione da inta a byteè una conversione con perdita di dati (vale a dire che le informazioni vengono perse). Pertanto, non è possibile riconvertirlo nel suo intvalore originale .
truthadjustr,

23

il byte in Java è firmato, quindi ha un intervallo compreso tra -2 ^ 7 e 2 ^ 7-1, ovvero da -128 a 127. Dato che 132 è sopra 127, si finisce con 132-256 = -124. Cioè, essenzialmente 256 (2 ^ 8) viene aggiunto o sottratto fino a quando non rientra nell'intervallo.

Per ulteriori informazioni, potresti voler leggere sul complemento a due .


16

132 è al di fuori dell'intervallo di un byte che è compreso tra -128 e 127 (Byte.MIN_VALUE in Byte.MAX_VALUE) Invece il bit superiore del valore a 8 bit viene trattato come un segno che indica che in questo caso è negativo. Quindi il numero è 132 - 256 = -124.


5

ecco un metodo molto meccanico senza le teorie distrattive:

  1. Converti il ​​numero in rappresentazione binaria (usa una calcolatrice ok?)
  2. Copiare solo gli 8 bit più a destra (LSB) e scartare il resto.
  3. Dal risultato del passaggio n. 2, se il bit più a sinistra è 0, utilizzare una calcolatrice per convertire il numero in decimale. Questa è la tua risposta
  4. Altrimenti (se il bit più a sinistra è 1) la tua risposta è negativa. Lascia invariati tutti gli zeri più a destra e il primo bit diverso da zero. E invertito il resto, ovvero sostituisce 1 con 0 e 0 con 1. Quindi utilizzare una calcolatrice per convertire in decimale e aggiungere un segno negativo per indicare che il valore è negativo.

Questo metodo più pratico è conforme alle molte risposte teoriche sopra. Quindi, quelli che stanno ancora leggendo quei libri Java che dicono di usare il modulo, questo è sicuramente sbagliato poiché i 4 passaggi che ho descritto sopra non sono sicuramente un'operazione di modulo.


Quali libri Java dicono di usare 'modulo'? Non ho mai visto alcun libro CS che affermi che in 46 anni, figuriamoci in qualsiasi libro Java. Quale 'modulo'? Non esiste alcuna operazione modulo in Java. Solo un operatore residuo.
Marchese di Lorne,

grep più forte. http://iiti.ac.in/people/~tanimad/JavaTheCompleteReference.pdfpagina 59
truthadjustr

4

Equazione del complemento a due:

inserisci qui la descrizione dell'immagine


In Java, byte(N = 8) e int(N = 32) sono rappresentati dal complemento 2s mostrato sopra.

Dall'equazione, un 7 è negativo per bytema positivo per 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

2

spesso nei libri troverai la spiegazione del casting da int a byte come eseguita dalla divisione del modulo. questo non è strettamente corretto, come mostrato di seguito, ciò che accade realmente è che i 24 bit più significativi dal valore binario del numero int vengono scartati lasciando confusione se viene impostato il bit più a sinistra rimanente che designa il numero come 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);

    }

}

}

2
Non l'ho mai visto in nessun libro in 46 anni.
Marchese di Lorne,

2

Un algoritmo rapido che simula il modo in cui funziona è il seguente:

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

Come funziona? Guarda la risposta daixtr . Un'implementazione dell'algoritmo esatto descritto nella sua risposta è la seguente:

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;
}

1

Se vuoi capirlo matematicamente, ad esempio come funziona

quindi in pratica i numeri in b / n da -128 a 127 verranno scritti come il loro valore decimale, al di sopra di quello (il tuo numero - 256).

per esempio. 132, la risposta sarà 132 - 256 = - 124 ie

256 + la tua risposta nel numero 256 + (-124) è 132

Un altro esempio

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

l'uscita sarà 39 44

(295 - 256) (300 - 256)

NOTA: non prenderà in considerazione i numeri dopo il decimale.


0

Concettualmente, al tuo numero vengono fatte ripetute sottrazioni di 256, fino a quando non è compreso tra -128 e +127. Quindi, nel tuo caso, inizi con 132, quindi finisci con -124 in un solo passaggio.

Computazionalmente, ciò corrisponde all'estrazione degli 8 bit meno significativi dal tuo numero originale. (E nota che il bit più significativo di questi 8 diventa il bit di segno.)

Si noti che in altre lingue questo comportamento non è definito (ad es. C e C ++).


Per essere chiari, il risultato che ottieni è lo stesso di una ripetizione di sottrazioni. In pratica, la JVM non lo fa in questo modo. (Sarebbe terribilmente inefficiente!)
Stephen C

Infatti. Spero che il mio secondo paragrafo riguardi come la JVM lo fa effettivamente. Ma ho un po 'armeggiato con la mia lingua.
Bathsheba,

1
Sì. Il passaggio da "essenzialmente" a "concettualmente" fa una differenza enorme!
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           

La risposta corretta si ottiene mascherando i bit, non per divisione e resto.
Marchese di Lorne,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.