Können wir in Java ein vorzeichenloses Byte erstellen?

185

Ich versuche, ein signiertes Byte in ein nicht signiertes zu konvertieren. Das Problem ist, dass die Daten, die ich empfange, nicht signiert sind und Java kein vorzeichenloses Byte unterstützt. Wenn es also die Daten liest, behandelt es sie als signiert.

Ich habe versucht, es mit der folgenden Lösung zu konvertieren, die ich von Stack Overflow erhalten habe.

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

Aber wenn es wieder in Byte konvertiert wird, erhalte ich die gleichen signierten Daten. Ich versuche, diese Daten als Parameter für eine Java-Funktion zu verwenden, die nur ein Byte als Parameter akzeptiert, daher kann ich keinen anderen Datentyp verwenden. Wie kann ich dieses Problem beheben?

dln
quelle
2
Guave: UnsignedBytes.toint (Byte-Wert)
Jacktrades
19
java.lang.Byte.toUnsignedInt (Bytewert);
themarketka

Antworten:

107

Ich bin mir nicht sicher, ob ich deine Frage verstehe.

Ich habe es gerade versucht und für Byte -12 (vorzeichenbehafteter Wert) wurde die Ganzzahl 244 zurückgegeben (entspricht dem vorzeichenlosen Bytewert, wird jedoch als int) eingegeben :

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

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

Ist es das, was du tun willst?

Java erlaubt es nicht , 244 als auszudrücken byteWert, wie für C. Um positive ganze Zahlen oben ausdrücken Byte.MAX_VALUE(127) Sie haben einen anderen Integer - Typen zu verwenden, wie short, intoder long.

Guillaume
quelle
1
byte b = (byte)unsignedToBytes((byte) -12); Versuchen Sie jetzt, b
Jigar Joshi
101
Warum haben Sie dies als die richtige Antwort akzeptiert? Alles, was es tut, ist genau das Gleiche wie die Methode, die Sie in Ihrer Frage erwähnt haben - konvertieren Sie ein Byte in eine vorzeichenlose Ganzzahl.
Adamski
1
Es ist wichtig, manchmal signierte Werte zu haben, manchmal nicht signierte. Wahrscheinlich ist dies der Grund, warum er diese Antwort akzeptiert hat. (Byte) (b & 0xff) hat keinen Sinn, aber (Byte) (Math.min ((b & 0xff) * 2, 255)) hat Sinn, z. B. in Computergrafiken wird nur das Pixel dargestellt durch das Byte zweimal heller. :-)
iirekm
3
Es könnte auch byteToUnsigned genannt werden
Hernán Eche
193

Die Tatsache, dass Grundelemente in Java signiert sind, spielt keine Rolle für die Darstellung im Speicher / Transit - ein Byte besteht lediglich aus 8 Bit, und ob Sie dies als signierten Bereich interpretieren oder nicht, liegt bei Ihnen. Es gibt keine magische Flagge, die sagt "dies ist signiert" oder "dies ist nicht signiert".

Wenn Grundelemente signiert sind, verhindert der Java-Compiler, dass Sie einem Byte einen Wert über +127 (oder unter -128) zuweisen können. Es gibt jedoch nichts, was Sie davon abhält, einen Int (oder Short) herunterzuspielen, um dies zu erreichen:

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
quelle
5
Für viele Operationen macht es keinen Unterschied, für einige Operationen jedoch. In beiden Fällen können Sie ein Byte ohne Vorzeichen oder ein Zeichen ohne Vorzeichen verwenden.
Peter Lawrey
61
Der Zugriff auf ein Array mit einer möglicherweise negativen Nummer ist nicht irrelevant.
Stefan
3
@Stefan - Ich meinte irrelevant im Zusammenhang mit der Darstellung auf dem Draht.
Adamski
5
Was für die Frage etwas irrelevant ist. Da er erwähnte, dass er es an eine Funktion übergeben muss, die nur Byte-Parameter akzeptiert, spielt es keine Rolle, ob das Wetter es ist, interpretieren wir es als Byte-Darstellung eines Einhorns. Java behandelt es immer als vorzeichenbehaftete Zahl, was beispielsweise problematisch sein kann, wenn diese Funktion den Parameter als Index verwendet. Um fair zu sein, habe ich auch die anderen Top 2 Antworten abgelehnt, da sie die Frage auch nicht beantworten.
Stefan
2
@Stefan +1 für dich. Absolut relevant, wenn Sie das Byte für den Zugriff auf ein Array mit 256 Elementen verwenden. Das ist ein hervorragendes Beispiel, um zu demonstrieren, warum jeder anfangen sollte, C und C ++ zu lernen, bevor er zu Java oder C # wechselt
Gianluca Ghettini
46

Die Java-Sprache bietet so etwas wie das unsignedSchlüsselwort nicht. A bytegemäß der Sprachspezifikation stellt einen Wert zwischen –128 - 127 dar. Wenn beispielsweise a bytein intJava umgewandelt wird, wird das erste Bit als Zeichen interpretiert und die Zeichenerweiterung verwendet .

Abgesehen davon hindert Sie nichts daran, byteeinfach 8 Bits anzuzeigen und diese Bits als Wert zwischen 0 und 255 zu interpretieren. Denken Sie jedoch daran, dass Sie nichts tun können, um Ihre Interpretation der Methode eines anderen aufzuzwingen. Wenn eine Methode a akzeptiert byte, akzeptiert diese Methode einen Wert zwischen –128 und 127, sofern nicht ausdrücklich anders angegeben.

Hier sind einige nützliche Konvertierungen / Manipulationen für Ihre Bequemlichkeit:

Konvertierungen von / nach 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

(Wenn Sie Java 8+ verwenden, verwenden Sie Byte.toUnsignedInt.)

Analysieren / Formatieren

Am besten verwenden Sie die oben genannten Konvertierungen:

// Parse an unsigned byte
byte b = (byte) Integer.parseInt("200");

// Print an unsigned byte
System.out.println("Value of my unsigned byte: " + (b & 0xFF));

Arithmetik

Die 2-Komplement-Darstellung "funktioniert einfach" für Addition, Subtraktion und Multiplikation:

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

Die Division erfordert die manuelle Konvertierung von Operanden:

byte ratio = (byte) ((b1 & 0xFF) / (b2 & 0xFF));
aioobe
quelle
1
'char' steht nicht für eine Zahl.
Abmeldung
26
Um es kurz zu machen: Du liegst falsch .
Aioobe
36

In Java gibt es keine primitiven vorzeichenlosen Bytes. Das Übliche ist, es auf einen größeren Typ zu werfen:

int anUnsignedByte = (int) aSignedByte & 0xff;
Peter Knego
quelle
Ist die Besetzung zu einem int notwendig?
Nich
Es kann eine implizite Besetzung sein, aber es gibt eine Besetzung in beide Richtungen. Und diese Besetzung hat eine signierte Erweiterung. Und das ist ein Problem. Wenn Sie eine explizite Besetzung durchführen, können Sie zumindest sehen, dass dies geschieht.
foo
4

Eine Randnotiz, wenn Sie es ausdrucken möchten, können Sie einfach sagen

byte b = 255;
System.out.println((b < 0 ? 256 + b : b));
Kyle Kinkade
quelle
6
warum so komplex? println(b & 0xff)ist genug
phuclv
0

Wenn Sie denken, Sie suchen nach so etwas.

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

Adamski lieferte die beste Antwort, aber sie ist nicht ganz vollständig. Lesen Sie daher seine Antwort, da sie die Details erklärt, die ich nicht bin.

Wenn Sie eine Systemfunktion haben, für die ein vorzeichenloses Byte übergeben werden muss, können Sie ein vorzeichenbehaftetes Byte übergeben, da es automatisch als vorzeichenloses Byte behandelt wird.

Wenn eine Systemfunktion vier Bytes benötigt, z. B. 192 168 0 1 als vorzeichenlose Bytes, können Sie -64 -88 0 1 übergeben, und die Funktion funktioniert weiterhin, da sie beim Übergeben an die Funktion die Signatur aufhebt .

Es ist jedoch unwahrscheinlich, dass dieses Problem auftritt, da Systemfunktionen aus Gründen der plattformübergreifenden Kompatibilität hinter Klassen verborgen sind, obwohl einige der Lesemethoden von java.io nicht seufzende Bytes als int zurückgeben.

Wenn Sie möchten, dass dies funktioniert, schreiben Sie signierte Bytes in eine Datei und lesen Sie sie als nicht signierte Bytes zurück.

Mutterschaf Loon
quelle
1
Es gibt keine vorzeichenbehafteten oder vorzeichenlosen Bytes.
Vlastimil Ovčáčík
Wie genau haben Sie die Bytes in Ihrem Beispiel geschrieben und gelesen?
Vlastimil Ovčáčík
0

Du kannst auch:

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

Erläuterung:

sagen wir a = (byte) 133;

Im Speicher wird gespeichert als: "1000 0101" (0x85 in hex)

Seine Darstellung übersetzt also vorzeichenlos = 133, signiert = -123 (als 2er-Komplement)

a << 24

Wenn die Linksverschiebung 24 Bit nach links ausgeführt wird, ist das Ergebnis nun eine 4-Byte-Ganzzahl, die wie folgt dargestellt wird:

"10000101 00000000 00000000 00000000" (oder "0x85000000" in hexadezimal)

dann haben wir

(a << 24) >>> 24

und es verschiebt sich wieder um die rechten 24 Bits, füllt sich aber mit führenden Nullen. So ergibt sich:

"00000000 00000000 00000000 10000101" (oder "0x00000085" in hex)

und das ist die vorzeichenlose Darstellung, die 133 entspricht.

Wenn Sie versuchen würden zu gießen, a = (int) a; würde es passieren, dass die 2er-Komplementdarstellung des Bytes beibehalten und als int auch als 2er-Komplement gespeichert wird:

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

Und das bedeutet: -123

mark_infinite
quelle
2
Im Jahr 2019 ist dies nicht erforderlich. Einfach benutzen java.lang.Byte.toUnsignedInt(byte value). Wenn Sie Java 8 noch nicht verwenden, aktualisieren Sie ASAP. Java 7 und früher sind am Ende der Lebensdauer.
Stephen C
0

Ich versuche, diese Daten als Parameter für eine Java-Funktion zu verwenden, die nur ein Byte als Parameter akzeptiert

Dies unterscheidet sich nicht wesentlich von einer Funktion, die eine Ganzzahl akzeptiert, an die Sie einen Wert übergeben möchten, der größer als 2 ^ 32-1 ist.

Das klingt so, als ob es davon abhängt, wie die Funktion definiert und dokumentiert ist. Ich kann drei Möglichkeiten sehen:

  1. Es kann explizit dokumentieren, dass die Funktion das Byte als vorzeichenlosen Wert behandelt. In diesem Fall sollte die Funktion wahrscheinlich das tun, was Sie erwarten, scheint aber falsch implementiert zu sein. Für den Ganzzahlfall würde die Funktion den Parameter wahrscheinlich als Ganzzahl ohne Vorzeichen deklarieren, für den Bytefall ist dies jedoch nicht möglich.

  2. Es kann dokumentiert werden, dass der Wert für dieses Argument größer als (oder vielleicht gleich) Null sein muss. In diesem Fall missbrauchen Sie die Funktion (übergeben einen Parameter außerhalb des Bereichs) und erwarten, dass sie mehr tut, als sie entworfen wurde machen. Bei einem gewissen Grad an Debugging-Unterstützung können Sie erwarten, dass die Funktion eine Ausnahme auslöst oder eine Zusicherung fehlschlägt.

  3. Die Dokumentation kann nichts sagen. In diesem Fall ist ein negativer Parameter ein negativer Parameter, und ob dies eine Bedeutung hat, hängt davon ab, was die Funktion tut. Wenn dies bedeutungslos ist, sollte die Funktion möglicherweise wirklich als (2) definiert / dokumentiert werden. Wenn dies auf nicht offensichtliche Weise sinnvoll ist (z. B. werden nicht negative Werte verwendet, um in ein Array zu indizieren, und negative Werte werden verwendet, um vom Ende des Arrays zurück zu indizieren, sodass -1 das letzte Element bedeutet), sollte in der Dokumentation angegeben werden, was es ist bedeutet und ich würde erwarten, dass es sowieso nicht das ist, was Sie wollen.

Kevin Martin
quelle
Hmmm, ich glaube, ich habe gerade eine Antwort gepostet, die für eine andere Frage zur Signatur von Bytes gedacht war, aber ich nehme an, dass sie auch hier noch ein bisschen relevant ist ...
Kevin Martin
-1

Wenn Sie eine Funktion haben, der ein vorzeichenbehaftetes Byte übergeben werden muss, was erwarten Sie dann, wenn Sie ein vorzeichenloses Byte übergeben?

Warum können Sie keinen anderen Datentyp verwenden?

Ungewöhnlich können Sie ein Byte als vorzeichenloses Byte mit einfachen oder keinen Übersetzungen verwenden. Es hängt alles davon ab, wie es verwendet wird. Sie müssten klären, was Sie damit vorhaben.

Peter Lawrey
quelle
-1

Obwohl es ärgerlich erscheinen mag (von C kommend), dass Java kein vorzeichenloses Byte in die Sprache aufgenommen hat, ist es wirklich keine große Sache, da eine einfache "b & 0xFF" -Operation den vorzeichenlosen Wert für (vorzeichenbehaftetes) Byte b im (seltenen) liefert. Situationen, die es tatsächlich benötigt. Die Bits ändern sich nicht wirklich - nur die Interpretation (was nur wichtig ist, wenn zum Beispiel einige mathematische Operationen an den Werten ausgeführt werden).

Bob
quelle
Schauen Sie, andere antworten, Sie denken, Ihre Antwort ist am besten / hilfreich? beschreiben Sie in wenig und fügen Sie es in Kommentaren hinzu
Jubin Patel
8
Es ist nicht selten, nur weil Sie nicht darauf gestoßen sind. Wenn Sie versuchen, ein Protokoll zu implementieren, werden Sie millionenfach darauf stoßen. Das Ärgerliche ist, dass die überwiegende Mehrheit der Anwendungsfälle, auf die ich gestoßen bin, sich mit Bytes befasst. Sie möchten sich mit vorzeichenlosen Bytes befassen (weil es sich um Bytes handelt, nicht um Zahlen). Das Verrückte ist, dass JEDE bitweise Operation es in ein int konvertiert, was bedeutet, dass alle "negativen" Werte beim Erweitern völlig andere Werte sind. Ja, Sie können es umgehen, indem Sie immer maskieren, aber es ist Zeitverschwendung, Prozessor und verursacht wirklich undurchsichtige Fehler, wenn Sie es vergessen.
Thor84no
Ich stimme Thor84no zu: Bytes sind keine Zahlen und sollten kein Vorzeichen haben. Auf der anderen Seite sollten wir, da es sich nicht um Zahlen handelt, nicht einmal die Operatoren + und - haben / verwenden. Die Verwendung nur bitweiser Operatoren funktioniert einwandfrei, auf der anderen Seite funktionieren Shift-Operatoren nicht wie gewünscht, und Java fördert tatsächlich ein verschobenes Byte zu einem int.
user1708042
1
@ VlastimilOvčáčík Das ist in diesem Fall buchstäblich unmöglich, das ist die aufregende Sache. Sie wiederholen x & 0xFFentweder überall, wo Sie es brauchen, oder Sie wiederholen so etwas wie behaveLikeAnUnsignedByte(x)überall. Dies ist für jede einzelne Stelle erforderlich, an der Sie einen Bytewert oder ein Byte-Array verwenden, das ohne Vorzeichen sein muss. Es ist nicht vorstellbar, diese Wiederholung zu vermeiden. Sie können die Implementierung eines Protokolls, das Bytewerte liest und schreibt, nicht mit nur einem einzigen Verweis auf eine Bytevariable schreiben. Ihre vereinfachte Sichtweise könnte erklären, warum sie sich nie darum gekümmert haben, das Problem zu beheben.
Thor84no
-1

In Java gibt es kein vorzeichenloses Byte. Wenn Sie jedoch ein Byte anzeigen möchten, können Sie Folgendes tun:

int myInt = 144;

byte myByte = (byte) myInt;

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

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

Ausgabe:

myChar : 90

Weitere Informationen finden Sie unter Anzeigen eines Hex / Byte-Werts in Java .

Jyo der Hauch
quelle
Sie müssen dies nicht selbst definieren. java.lang.Byte.toUnsignedInt(byte value);existiert dafür.
Alexander - Reinstate Monica
-2

Gemäß den Einschränkungen in Java ist ein vorzeichenloses Byte im aktuellen Datentypformat fast unmöglich. Sie können für das, was Sie implementieren, nach anderen Bibliotheken einer anderen Sprache suchen und diese dann mit JNI aufrufen .

Pritesh Jain
quelle
Ich glaube nicht, dass er es als signiertes Byte speichern möchte. Er empfängt es als signiertes Byte und möchte es als int speichern, was vollkommen gültig ist. Sein Problem ist, dass überall dort, wo er Eingaben erhält, ein Wert zwischen 0 und 255 als Byte dargestellt wird, Java dies jedoch als zu zweit ergänzenden vorzeichenbehafteten Wert interpretiert, da Java keine vorzeichenbehafteten Bytes unterstützt.
Zac
-2

Ja und nein. Ich habe mit diesem Problem herumgegraben. Wie ich das verstehe:

Tatsache ist, dass Java Interger -128 bis 127 signiert hat. Es ist möglich, ein in Java nicht signiertes zu präsentieren mit:

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

Wenn Sie zum Beispiel -12 signierte Nummer hinzufügen, um nicht signiert zu sein, erhalten Sie 244. Sie können diese Nummer jedoch erneut in signiert verwenden. Sie muss wieder auf signiert verschoben werden und wird erneut -12 sein.

Wenn Sie versuchen, dem Java-Byte 244 hinzuzufügen, erhalten Sie outOfIndexException.

Prost..

Sindri Þór
quelle
3
Sie müssen dies nicht selbst definieren. java.lang.Byte.toUnsignedInt(byte value);existiert dafür.
Alexander - Monica
-3

Wenn Sie vorzeichenlose Bytes in Java möchten, subtrahieren Sie einfach 256 von der Zahl, an der Sie interessiert sind. Es wird ein Zweierkomplement mit einem negativen Wert erzeugt, der die gewünschte Zahl in vorzeichenlosen Bytes ist.

Beispiel:

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.

Sie müssen solche schmutzigen Hacks verwenden, wenn Sie leJOS verwenden , um den NXT- Baustein zu programmieren .

XapaJIaMnu
quelle
Sie erkennen, dass der Binärwert von 255 auch 1111 1111 ist, also ist keine Subtraktion erforderlich, oder?
Nick White
@ NickWhite, ja in binär. Aber Java verwendet die 2-Ergänzung, wobei 255 nicht 11111111 ist
XapaJIaMnu
Entschuldigung, aber das ist einfach falsch. Versuchen Sie einige Experimente. Der Wert in speed_unsignedist signiert. Drucken Sie es aus und sehen Sie. (Und das - 256erreicht hier nichts.)
Stephen C