Seltsames Verhalten, wenn Java int in Byte konvertiert?

130
int i =132;

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

Verblüffend. Warum ist die Ausgabe -124?

hart
quelle

Antworten:

172

In Java intist an 32 Bit. A byteist 8 bits.

Die meisten primitiven Typen in Java angemeldet sind, und byte, short, intund longwerden in Zweier-Komplement codiert. (Der charTyp ist nicht signiert und das Konzept eines Zeichens gilt nicht für boolean.)

In diesem Zahlenschema gibt das höchstwertige Bit das Vorzeichen der Zahl an. Wenn mehr Bits benötigt werden, wird das höchstwertige Bit ("MSB") einfach in das neue MSB kopiert.

Wenn Sie also Byte haben 255: 11111111 und es als int(32 Bit) darstellen möchten, kopieren Sie einfach die 1 24 Mal nach links.

Eine Möglichkeit, die Komplementzahl einer negativen Zwei zu lesen, besteht darin, mit dem niedrigstwertigen Bit zu beginnen, nach links zu gehen, bis Sie die erste 1 finden, und anschließend jedes Bit zu invertieren. Die resultierende Zahl ist die positive Version dieser Zahl

Zum Beispiel: 11111111geht zu 00000001= -1. Dies ist, was Java als Wert anzeigt.

Was Sie wahrscheinlich tun möchten, ist den vorzeichenlosen Wert des Bytes zu kennen.

Sie können dies mit einer Bitmaske erreichen, die alles außer den niedrigstwertigen 8 Bits löscht. (0xff)

So:

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

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

Würde ausdrucken: "Signed: -1 Unsigned: 255"

Was passiert hier eigentlich?

Wir verwenden bitweises UND, um alle Fremdzeichenbits zu maskieren (die Einsen links von den niedrigstwertigen 8 Bits). Wenn ein int in ein Byte konvertiert wird, schneidet Java die 24 Bits ganz links ab

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

Da das 32. Bit jetzt das Vorzeichenbit anstelle des 8. Bits ist (und wir das Vorzeichenbit auf 0 setzen, was positiv ist), werden die ursprünglichen 8 Bits aus dem Byte von Java als positiver Wert gelesen.

Wayne
quelle
1
Gut gemacht, die beste Erklärung zu diesem Thema, Wayne! Ich suche nur nach der mathematischen Formalisierung, warum in einer Zweierkomplementdarstellung das Vorzeichenbit rechts kopiert werden kann, um Bits hinzuzufügen. Es ist leicht zu verstehen, wenn man nach der Regel denkt, wie man das Negativ einer Zahl erhält. Das heißt: Betrachten Sie alle Bits von rechts nach links und schreiben Sie sie unverändert, bis die erste 1 enthalten ist. Invertieren Sie dann die nachfolgenden Bits. Wenn ich das fehlende Bit als Null betrachte, ist es leicht zu verstehen, dass alle auf 1 gehen. Aber ich suchte nach einer 'mathematischeren' Erklärung.
AgostinoX
Was hier passiert, signedByte & (0xff)ist, dass 0xffes sich um ein Interger-Literal handelt. SignedByte wird daher zu einer Ganzzahl heraufgestuft, bevor die bitweise Operation ausgeführt wird.
Kevin Wheeler
Das ist nicht 0xFF, sondern 0x7E in Ihrem Beispiel!
JohnyTex
89

132in Ziffern ( Basis 10 ) ist 1000_0100in Bits ( Basis 2 ) und Java speichert intin 32 Bits:

0000_0000_0000_0000_0000_0000_1000_0100

Der Algorithmus für Int-to-Byte ist links abgeschnitten. Der Algorithmus für System.out.printlnist das Zweierkomplement (das Zweierkomplement ist, wenn das Bit ganz links ist 1, als negatives Einerkomplement (invertierte Bits) minus eins zu interpretieren .); Also System.out.println(int-to-byte( ))ist:

  • interpret-as (if-left-bit-is-1 [negativ (Invert-Bits (minus-eins (] left-truncate ( 0000_0000_0000_0000_0000_0000_1000_0100) [)))])
  • = interpretiere-als (wenn-ganz-links-Bit-ist-1 [negativ (Invert-Bits (minus-eins (] 1000_0100[)))])
  • = interpret-as (negativ (Invertierungsbits (minus eins ( 1000_0100))))
  • = interpret-as (negativ (invert-bits ( 1000_0011)))
  • = interpretiere als (negativ ( 0111_1100))
  • = interpretieren als (negativ (124))
  • = interpretiere als (-124)
  • = -124 Tada !!!
Pacerier
quelle
7
Sehr schön erklärt
ZAJ
1
Jetzt ist 132 in Dezimalzahl -124 in Byte. Wie funktioniert das Gegenteil?
Nilesh Deokar
@NileshDeokar, Das Gegenteil ist von POLA, da sie passen (vgl. JLS 5.1.2 ); Die Ausgabe stimmt mit dem linken Vorzeichen überein ( 0für positiv und 1für negativ).
Pacerier
Was ist POLA? Die Konvertierung von intnach a byteist eine verlustbehaftete Konvertierung (dh Informationen gehen verloren). Daher gibt es keine Möglichkeit, es wieder in seinen ursprünglichen intWert umzuwandeln .
Truthadjustr
23

Das Byte in Java ist signiert, hat also einen Bereich von -2 ^ 7 bis 2 ^ 7-1 - dh -128 bis 127. Da 132 über 127 liegt, werden Sie am Ende um 132-256 = -124 gewickelt. Das heißt, im Wesentlichen werden 256 (2 ^ 8) addiert oder subtrahiert, bis sie in den Bereich fallen.

Für weitere Informationen möchten Sie vielleicht das Zweierkomplement nachlesen .

bdonlan
quelle
16

132 liegt außerhalb des Bereichs eines Bytes, das zwischen -128 und 127 liegt (Byte.MIN_VALUE bis Byte.MAX_VALUE). Stattdessen wird das oberste Bit des 8-Bit-Werts als Vorzeichen behandelt, was anzeigt, dass es in diesem Fall negativ ist. Die Zahl ist also 132 - 256 = -124.

Peter Lawrey
quelle
5

Hier ist eine sehr mechanische Methode ohne die ablenkenden Theorien:

  1. Wandle die Zahl in eine binäre Darstellung um (benutze einen Taschenrechner ok?)
  2. Kopieren Sie nur die 8 Bits (LSB) ganz rechts und verwerfen Sie den Rest.
  3. Wenn das Bit ganz links im Ergebnis von Schritt 2 0 ist, konvertieren Sie die Zahl mit einem Taschenrechner in eine Dezimalzahl. Das ist deine Antwort.
  4. Andernfalls (wenn das Bit ganz links 1 ist) ist Ihre Antwort negativ. Lassen Sie alle Nullen ganz rechts und das erste Nicht-Null-Bit unverändert. Und umgekehrt den Rest, dh 1 durch 0 und 0 durch 1 ersetzen. Verwenden Sie dann einen Taschenrechner, um in eine Dezimalzahl umzuwandeln, und fügen Sie ein negatives Vorzeichen hinzu, um anzuzeigen, dass der Wert negativ ist.

Diese praktischere Methode entspricht den vielen theoretischen Antworten oben. Wenn Sie also noch die Java-Bücher lesen, in denen es heißt, Modulo zu verwenden, ist dies definitiv falsch, da die oben beschriebenen 4 Schritte definitiv keine Modulo-Operation sind.

trueadjustr
quelle
Welche Java-Bücher sagen "Modulo"? Ich habe noch nie ein CS-Buch gesehen, das dies in 46 Jahren besagt, geschweige denn ein Java-Buch. Was für ein Modulo? In Java gibt es keine Modulo-Operation. Nur ein Restoperator.
Marquis von Lorne
grep härter. http://iiti.ac.in/people/~tanimad/JavaTheCompleteReference.pdfSeite 59
Truthadjustr
4

Zweierkomplement Gleichung:

Geben Sie hier die Bildbeschreibung ein


In Java werden byte(N = 8) und int(N = 32) durch das oben gezeigte 2s-Komplement dargestellt.

Aus der Gleichung ergibt sich eine 7 negativ für, byteaber positiv für 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
quelle
2

Oft finden Sie in Büchern die Erklärung für das Casting von int nach byte als durch Modulteilung ausgeführt. Dies ist nicht genau richtig, wie unten gezeigt. Was tatsächlich passiert, ist, dass die 24 höchstwertigen Bits aus dem Binärwert der int-Zahl verworfen werden, was Verwirrung stiftet, wenn das verbleibende Bit ganz links gesetzt wird, das die Zahl als negativ bezeichnet

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

    }

}

}
Stift
quelle
2
Ich habe das seit 46 Jahren in keinem Buch mehr gesehen.
Marquis von Lorne
2

Ein schneller Algorithmus, der die Funktionsweise simuliert, ist folgender:

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

Wie funktioniert das? Schauen Sie auf daixtr Antwort. Eine Implementierung des genauen Algorithmus, der in seiner Antwort beschrieben ist, ist die folgende:

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
quelle
1

Wenn Sie dies mathematisch verstehen möchten, wie dies funktioniert

Im Grunde genommen werden die Zahlen s / w -128 bis 127 genauso geschrieben wie ihr Dezimalwert, über dem Wert (Ihre Zahl - 256).

z.B. 132 wird die Antwort 132 - 256 = - 124 sein, dh

256 + Ihre Antwort in der Zahl 256 + (-124) ist 132

Ein anderes Beispiel

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

Die Ausgabe wird 39 44 sein

(295 - 256) (300 - 256)

HINWEIS: Zahlen nach der Dezimalstelle werden nicht berücksichtigt.

Prakhar Lohiya
quelle
0

Konzeptionell werden wiederholte Subtraktionen von 256 von Ihrer Zahl vorgenommen, bis sie im Bereich von -128 bis +127 liegt. In Ihrem Fall beginnen Sie also mit 132 und enden dann in einem Schritt mit -124.

Rechnerisch entspricht dies dem Extrahieren der 8 niedrigstwertigen Bits aus Ihrer ursprünglichen Nummer. (Und beachten Sie, dass das höchstwertige Bit dieser 8 das Vorzeichenbit wird.)

Beachten Sie, dass dieses Verhalten in anderen Sprachen nicht definiert ist (z. B. C und C ++).

Bathseba
quelle
Das Ergebnis ist das gleiche, als ob wiederholte Subtraktionen durchgeführt würden. In der Praxis macht die JVM dies nicht so. (Es wäre schrecklich ineffizient!)
Stephen C
Tatsächlich. Ich hoffe, mein zweiter Absatz behandelt, wie die JVM dies tatsächlich tut. Aber ich habe ein wenig an meiner Sprache herumgespielt.
Bathseba
1
Ja. Der Wechsel von "im Wesentlichen" zu "konzeptionell" macht einen großen Unterschied!
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           
Teufel_29
quelle
Die richtige Antwort wird durch Bitmaskierung erhalten, nicht durch Division und Rest.
Marquis von Lorne