So konvertieren Sie Int in vorzeichenloses Byte und zurück

92

Ich muss eine Zahl in ein vorzeichenloses Byte konvertieren. Die Zahl ist immer kleiner oder gleich 255 und passt daher in ein Byte.

Ich muss dieses Byte auch wieder in diese Zahl konvertieren. Wie würde ich das in Java machen? Ich habe verschiedene Möglichkeiten ausprobiert und keine funktioniert. Folgendes versuche ich jetzt zu tun:

int size = 5;
// Convert size int to binary
String sizeStr = Integer.toString(size);
byte binaryByte = Byte.valueOf(sizeStr);

und nun, um dieses Byte wieder in die Zahl umzuwandeln:

Byte test = new Byte(binaryByte);
int msgSize = test.intValue();

Dies funktioniert natürlich nicht. Aus irgendeinem Grund wird die Zahl immer in konvertiert 65. Irgendwelche Vorschläge?

dunkler Himmel
quelle
Ich habe auch diese Methode ausprobiert : crazysquirrel.com/computing/java/basics/… - funktioniert nicht.
Darksky

Antworten:

199

Ein Byte ist immer in Java signiert. Sie können den vorzeichenlosen Wert erhalten, indem Sie ihn mit 0xFF binär verknüpfen:

int i = 234;
byte b = (byte) i;
System.out.println(b); // -22
int i2 = b & 0xFF;
System.out.println(i2); // 234
JB Nizet
quelle
Ich werde immer noch 65 ... Warum ist das so?
Darksky
1
Es speichert die gewünschte Nummer im Byte. Java betrachtet es nur als vorzeichenbehaftete Nummer und nicht als vorzeichenlose. Aber jedes Bit ist das gleiche, als wäre es eine vorzeichenlose Zahl. Ihre Zeichenfolgenkonvertierung in Bytes ist eine weitere, nicht verwandte Frage. Stellen Sie es separat in eine neue Frage.
JB Nizet
1
Verwenden Sie getInt, um Ihr int zurückzugewinnen und das int als Byte zu transformieren. Ein int ist ein int. Woher es kommt, spielt keine Rolle.
JB Nizet
18
Warum führt bitweise und mit 0xFFzu einer vorzeichenlosen Ganzzahl? Eine Erklärung wäre wirklich hilfreich.
user462455
2
Java 8 verwendet dieselbe Methode: public static int toUnsignedInt (Byte x) {return ((int) x) & 0xff; }
Daniel De León
55

Java 8 bietet Byte.toUnsignedIntkonvertieren bytezu intdurch unsigned Konvertierung. Im JDK von Oracle wird dies einfach implementiert, return ((int) x) & 0xff;da HotSpot bereits versteht, wie dieses Muster optimiert werden kann, es jedoch auf anderen VMs vorhanden sein kann. Noch wichtiger ist, dass keine Vorkenntnisse erforderlich sind, um zu verstehen, was ein Anruf toUnsignedInt(foo)bewirkt.

Insgesamt Java 8 stellt Methoden zu konvertieren byteund shortzu unsigned intund long, und intin unsigned long. Eine Methode zum Konvertieren bytein vorzeichenlose shortwurde absichtlich weggelassen, da die JVM nur intund longohnehin Arithmetik bereitstellt .

Um ein int wieder in ein Byte umzuwandeln, verwenden Sie einfach einen Cast : (byte)someInt. Die resultierende verengende primitive Konvertierung verwirft alle bis auf die letzten 8 Bits.

Jeffrey Bosboom
quelle
7

Wenn Sie nur einen erwarteten 8-Bit-Wert von einem vorzeichenbehafteten int in einen vorzeichenlosen Wert konvertieren müssen, können Sie die einfache Bitverschiebung verwenden:

int signed = -119;  // 11111111 11111111 11111111 10001001

/**
 * Use unsigned right shift operator to drop unset bits in positions 8-31
 */
int psuedoUnsigned = (signed << 24) >>> 24;  // 00000000 00000000 00000000 10001001 -> 137 base 10

/** 
 * Convert back to signed by using the sign-extension properties of the right shift operator
 */
int backToSigned = (psuedoUnsigned << 24) >> 24; // back to original bit pattern

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

Wenn Sie etwas anderes als intden Basistyp verwenden, müssen Sie natürlich den Verschiebungsbetrag anpassen: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

Denken Sie auch daran, dass Sie bytetype nicht verwenden können . Andernfalls erhalten Sie einen signierten Wert, wie von anderen Antwortenden angegeben. Der kleinste primitive Typ, den Sie zur Darstellung eines 8-Bit-Werts ohne Vorzeichen verwenden können, ist a short.

dm78
quelle
3

Der Integer.toString(size)Aufruf wird in die char-Darstellung Ihrer Ganzzahl konvertiert, dh in das char '5'. Die ASCII-Darstellung dieses Zeichens ist der Wert 65.

Sie müssen die Zeichenfolge zuerst auf einen ganzzahligen Wert zurücksetzen, z. B. mithilfe von Integer.parseInt, um den ursprünglichen int-Wert wiederherzustellen.

Unter dem Strich ist es für eine vorzeichenbehaftete / vorzeichenlose Konvertierung am besten, Stringdas Bild wegzulassen und die Bitmanipulation zu verwenden, wie @JB vorschlägt.

Péter Török
quelle
Also so? (Immer noch 65 hier)String sizeStr = Integer.toString(size); int sizeInt = Integer.parseInt(sizeStr); byte binaryByte = Byte.valueOf((byte) sizeInt);
Darksky
@Nayefc, ich habe meine Antwort gerade aktualisiert, als Sie Ihren Kommentar geschrieben haben :-)
Péter Török
Gut, danke! Aus irgendeinem Grund druckt es NOCH 65 aus. Was bedeutet das? Wenn nur 65 angezeigt wird, was tatsächlich im Byte gespeichert ist, verstehe ich es nicht. Bitte überprüfen Sie auch meine Frage, ich habe sie bearbeitet und einen Abschnitt über die Konvertierung von Zeichenfolgen hinzugefügt :)
darksky
@Nayefc String.getBytes()liefert wiederum die ASCII-Werte der im Text enthaltenen Zeichen , nicht die numerischen Werte der Ziffern. Sie müssen die Zeichenfolge in einen numerischen Wert analysieren , wie oben angedeutet.
Péter Török
1
@ Nayefc, das ist also eigentlich eine ganz andere Frage. Und AFAIS sollte es funktionieren. Erstellen Sie eine neue Frage, einschließlich der tatsächlichen Ein- und Ausgabe der Konvertierung und des vollständigen Codes, um den Fall zu reproduzieren.
Péter Török
3

Die Lösung funktioniert einwandfrei (danke!). Wenn Sie jedoch das Casting vermeiden und die Arbeit auf niedriger Ebene dem JDK überlassen möchten, können Sie Ihre Ints mit einem DataOutputStream schreiben und mit einem DataInputStream zurücklesen. Sie werden automatisch als nicht signiert behandelt Bytes dann:

Zum Konvertieren von Ints in Binärbytes;

ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
int val = 250;
dos.write(byteVal);
...
dos.flush();

Lesen Sie sie zurück in:

// important to use a (non-Unicode!) encoding like US_ASCII or ISO-8859-1,
// i.e., one that uses one byte per character
ByteArrayInputStream bis = new ByteArrayInputStream(
   bos.toString("ISO-8859-1").getBytes("ISO-8859-1"));
DataInputStream dis = new DataInputStream(bis);
int byteVal = dis.readUnsignedByte();

Esp. nützlich für den Umgang mit binären Datenformaten (z. B. flache Nachrichtenformate usw.)

Gregor
quelle
3

Außer char, dass alle anderen numerischen Datentypen in Java signiert sind.

Wie in einer vorherigen Antwort erwähnt, können Sie den vorzeichenlosen Wert erhalten, indem Sie eine andOperation mit ausführen 0xFF. In dieser Antwort werde ich erklären, wie es passiert.

int i = 234;
byte b = (byte) i;
System.out.println(b);  // -22

int i2 = b & 0xFF;      
// This is like casting b to int and perform and operation with 0xFF

System.out.println(i2); // 234

Wenn Ihr Computer 32-Bit ist, benötigt der intDatentyp 32-Bit, um Werte zu speichern. bytebenötigt nur 8-Bit.

Die intVariable iwird im Speicher wie folgt dargestellt (als 32-Bit-Ganzzahl).

0{24}11101010

Dann wird die byteVariable bdargestellt als:

11101010

Da bytes nicht signiert sind, repräsentiert dieser Wert -22. (Suchen Sie nach dem 2er-Komplement, um mehr darüber zu erfahren, wie negative Ganzzahlen im Speicher dargestellt werden.)

Dann, wenn Sie Casting ist, wird intes immer noch sein, -22weil Casting das Vorzeichen einer Zahl bewahrt.

1{24}11101010

Der gegossene 32-bitWert der Operation bausführen andmit 0xFF.

 1{24}11101010 & 0{24}11111111
=0{24}11101010

Dann bekommst du 234als Antwort.

Ramesh-X
quelle
1

Umgang mit Bytes und vorzeichenlosen Ganzzahlen mit BigInteger:

byte[] b = ...                    // your integer in big-endian
BigInteger ui = new BigInteger(b) // let BigInteger do the work
int i = ui.intValue()             // unsigned value assigned to i
pgrandjean
quelle
1

Auch wenn es zu spät ist, möchte ich meine Meinung dazu abgeben, da dies möglicherweise klarstellt, warum die von JB Nizet gegebene Lösung funktioniert. Ich bin auf dieses kleine Problem gestoßen, als ich an einem Byte-Parser arbeitete und die String-Konvertierung selbst durchführte. Wenn Sie von einem größeren Integraltyp in einen kleineren Integraltyp kopieren, wie in diesem Java-Dokument angegeben, geschieht Folgendes:

https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3 Bei einer engeren Konvertierung einer vorzeichenbehafteten Ganzzahl in einen integralen Typ T werden einfach alle außer dem n niedrigsten verworfen Ordnungsbits, wobei n die Anzahl der Bits ist, die zur Darstellung des Typs T verwendet werden. Zusätzlich zu einem möglichen Informationsverlust über die Größe des numerischen Werts kann dies dazu führen, dass das Vorzeichen des resultierenden Werts vom Vorzeichen des Eingabewerts abweicht .

Sie können sicher sein, dass ein Byte ein integraler Typ ist, da in diesem Java-Dokument https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html Byte angegeben ist: Der Byte-Datentyp ist ein 8-Bit-Zwei mit Vorzeichen Komplement Ganzzahl.

Wenn Sie also eine Ganzzahl (32 Bit) in ein Byte (8 Bit) umwandeln, kopieren Sie einfach die letzten (niedrigstwertigen 8 Bit) dieser Ganzzahl in die angegebene Bytevariable.

int a = 128;
byte b = (byte)a; // Last 8 bits gets copied
System.out.println(b);  // -128

Im zweiten Teil der Geschichte geht es darum, wie unäre und binäre Java-Operatoren Operanden fördern. https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2 Die Erweiterung der primitiven Konvertierung (§5.1.2) wird angewendet, um einen oder beide Operanden wie angegeben zu konvertieren nach folgenden Regeln:

Wenn einer der Operanden vom Typ double ist, wird der andere in double konvertiert.

Wenn einer der Operanden vom Typ float ist, wird der andere in float konvertiert.

Wenn einer der Operanden vom Typ long ist, wird der andere in long konvertiert.

Andernfalls werden beide Operanden in den Typ int konvertiert.

Seien Sie versichert, wenn Sie mit dem integralen Typ int und / oder niedriger arbeiten, wird dieser zu einem int heraufgestuft.

// byte b(0x80) gets promoted to int (0xFF80) by the & operator and then
// 0xFF80 & 0xFF (0xFF translates to 0x00FF) bitwise operation yields 
// 0x0080
a = b & 0xFF;
System.out.println(a); // 128

Ich habe mir auch den Kopf darüber gekratzt :). Hier gibt es eine gute Antwort von rgettman. Bitweise Operatoren in Java nur für Integer und Long?

KaziQ
quelle
0

Wenn Sie die primitiven Wrapper-Klassen verwenden möchten, funktioniert dies, aber alle Java-Typen sind standardmäßig signiert.

public static void main(String[] args) {
    Integer i=5;
    Byte b = Byte.valueOf(i+""); //converts i to String and calls Byte.valueOf()
    System.out.println(b);
    System.out.println(Integer.valueOf(b));
}
Romeo
quelle
-1

in Java 7

public class Main {
    public static void main(String[] args) {
        byte b =  -2;
        int i = 0 ;
        i = ( b & 0b1111_1111 ) ;
        System.err.println(i);
    }
}

Ergebnis: 254

Tristan De Smet
quelle