Inselupdate: Vorzeichenlos arbeiten

Bis auf char sind in Java alle integralen Datentypen vorzeichenbehaftet und kodiert im Zweierkomplement. Bei einem byte stehen 8 Bit für die Kodierung eines Wertes zur Verfügung, jedoch sind es eigentlich nur 7 Bit, denn über ein Bit erfolgt die Kodierung des Vorzeichens. Der Wertebereich ist von -128 bis +127. Über einen Umweg ist es möglich, den vollen Wertebereich auszuschöpfen und so zu tun, als ob Java vorzeichenlose Datentypen hätte.

byte als vorzeichenlosen Datentyp nutzen

Eine wichtige Eigenschaft der expliziten Typanpassung bei Ganzzahltypen ist es, dass die überschüssigen Bytes einfach abgeschnitten werden. Betrachten wir die Typanpassung an einem Beispiel:

int l = 0xABCD6F;

byte b = (byte) 0xABCD6F;

System.out.println( Integer.toBinaryString( l ) ); // 101010111100110101101111

System.out.println( Integer.toBinaryString( b ) ); // 1101111

Liegt eine Zahl im Bereich von 0 bis 255, so kann ein byte diese durch seine 8 Bit grundsätzlich speichern. Java muss jedoch mit der expliziten Typanpassung gezwungen werden, das Vorzeichenbit zu ignorieren. Erst dann entspricht die Zahl 255 acht gesetzten Bits, denn sie mit byte b = 255; zu belegen, funktioniert nicht.

Damit die Weiterverarbeitung gelingt, muss noch eine andere Eigenschaft berücksichtigt werden. Sehen wir uns dazu folgende Ausgabe an:

byte b1 = (byte) 255;

byte b2 = -1;

System.out.println( b1 ); // -1

System.out.println( b2 ); // -1

Das Bitmuster ist in beiden Fällen gleich, alle Bits sind gesetzt. Dass die Konsolenausgabe aber negativ ist, hat mit einer anderen Java-Eigenschaft zu tun: Java konvertiert das Byte, welches vorzeichenbehaftet ist, in ein int (der Parametertyp bei toBinaryString() ist int) und bei dieser Konvertierung wandert das Vorzeichen weiter. Das folgende Beispiel zeigt das bei der Binärausgabe:

byte b = (byte) 255;
int  i = 255;
System.out.printf( "%d %s%n", b, Integer.toBinaryString(b) );

// –1  11111111111111111111111111111111
System.out.printf( "%d %s%n", i, Integer.toBinaryString(i) );

// 255                         11111111

Die Belegung der unteren 8 Bit vom byte b und int i ist identisch. Aber während beim int die oberen 3 Byte wirklich null sind, füllt Java durch die automatische Anpassung des Vorzeichens bei der Konvertierung von byte nach int im Zweierkomplement auf die oberen drei Byte mit 255 auf. Soll ohne Vorzeichen weitergerechnet werden stört das. Diese Automatisch Anpassung nimmt Java immer vor, wenn mit byte/short gerechnet wird und nicht nur wie in unserem Beispiel, wenn eine Methoden den Datentyp int fordert.

Um bei der Weiterverarbeitung einen Datenwert zwischen 0 und 255 zu bekommen, also das Byte eines int vorzeichenlos zu sehen, schneiden wir mit der Und-Verknüpfung die unteren 8 Bit heraus – alle anderen Bits bleiben also ausgenommen:

byte b = (byte) 255;

System.out.println( b ); // -1

System.out.println( b & 0xff ); // 255

Bibliotheksmethoden für vorzeichenlose Behandlung

Immer ein & 0xff an einen Ausdruck zu setzen um die oberen Bytes auszublenden ist zwar nicht sonderlich aufwändig, aber schön ist das auch nicht. Hübscher sind Methoden wie toUnsignedInt(byte), die mit dem Namen deutlich dokumentieren, was hier eigentlich passiert. In Java 8 gibt es daher einige Neuerungen.

Neue Methoden in Byte:

  • static int toUnsignedInt(byte x)
  • static long toUnsignedLong(byte x)
  • In Integer:

  • static long toUnsignedLong(int x)
  • static String toUnsignedString(int i, int radix)
  • static String toUnsignedString(int i)
  • static int parseUnsignedInt(String s, int radix)
  • static int compareUnsigned(int x, int y)
  • static int divideUnsigned(int dividend, int divisor)
  • static int remainderUnsigned(int dividend, int divisor)
  • In Long:

  • String toUnsignedString(long i, int radix)
  • static String toUnsignedString(long i)
  • static long parseUnsignedLong(String s, int radix)
  • static int compareUnsigned(long x, long y)
  • static long divideUnsigned(long dividend, long divisor)
  • static long remainderUnsigned(long dividend, long divisor)
  • In Short:

  • static int toUnsignedInt(short x)
  • static long toUnsignedLong(short x)
  • Neben den einfachen Methoden toUnsignedXXX()-Methoden in den Wrapper-Klassen gesellen sich Methoden hinzu, die auch die Konvertierung in einem String bzw. das Parsen eines Strings ermöglichen. Bei Integer und Long lassen sich ebenfalls neue Methoden ablesen, die Vergleiche, Division und Restwertbildung vorzeichenlos durchführen.

    Ähnliche Beiträge

    Schreibe einen Kommentar

    Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.