Datentypen byte als unsigned interpretieren

Diskutiere byte als unsigned interpretieren im Allgemeine Java-Themen Bereich.
kodela

kodela

Hallo,

aus reinem Interesse an der Sache versuche ich ein vor vielen vielen Jahren von mir in C++ geschriebenes Schachprogramm nach Java zu übersetzen. Ich bin damit schon relativ weit gekommen, hänge jetzt aber an der Eröffnungsbibliothek.

Diese umfasst knapp 30.000 Züge und muss aus einer Datei eingelesen werden. Jedes Byte dieser Datei enthält in den ersten sechs Bits eine Zugnummer, über die der Zug festgestellt werden kann. Die beiden anderen Bits stehen für vier Attribute, über die Folgezüge und Varianten angezeigt werden.

In C++ werden die Bytes als Char-Typen eingelesen und auch behandelt. Da in Java der Typ Char 16 Bit hat, lese ich die Datei als Bytes ein. So lange für die Feststellung eines Zuges das Bit 7 keine Bedeutung hat, funktioniert alles. Das ändert sich mit dem letzten Byte einer Variante. Dieser Zustand wird durch das gesetzte Bit 7 angezeigt. Wenn aber das Bit 7 gesetzt ist, wird der gesamte Wert des Bytes als negativer Wert interpretiert, was naturgemäß zu einem falschen Ergebnis führen muss.

Ich könnte nun zwar die gesamte Datei mit der Eröffnungsbibliothek so speichern, dass jedes Byte als Short gespeichert und in Zukunft die Auswertung über diese Short-oder Char-Daten vorgenommen wird. Dies möchte ich aber nur als allerletzte Möglichkeit in Erwägung ziehen. Meine Versuche, die Byte-Daten per Casting nach Char doch als unsigned Werte zu behandeln, waren leider nicht erfolgreich.

Hat jemand eine Idee, wie ich dieses verflixte Bit 7 abfragen kann, ohne dass sich auch der übrige Wert ändert?

Hier der Code, mit dem ich die Eröffnungsbibliothek einlese:
Java:
// Einlesen der EB nach openings
try {
    File f = new File(libPath + File.separator + iniDat);
    long lenLib = f.length();
    if (lenLib == 0) {
        JOptionPane.showMessageDialog(null,
        "[InfoText]");
        return false;
    }
    oplibSize = (int)lenLib;
    openings = new byte[oplibSize];
    try (InputStream in = new FileInputStream(f)) {
        in.read(openings);
    }
} catch (IOException e) {
    JOptionPane.showMessageDialog(null,
    "[InfoText]");
    return false;
}
Und hier noch ein Beispiel für die Auswertung:
Java:
while ((((int)openings[libNo] & 0x3F) != opCount) &&    // solange Zug nicht gefunden
        ((int)openings[libNo] < 0x80)) {                // und Variante vorhanden
    nextLibNo(false);                                   // nächsten Variantenblock suchen
}
Schon im Voraus ein Danke für jede Idee, die mir hilft, meine EB auch unter Java in etwa so wie unter C++ verwenden zu können.

kodela
 
H

httpdigest

Java:
byte signedByte = ...;
int unsigned = signedByte & 0xFF;
In `unsigned` kannst du dann das achte Bit prüfen.
 
sascha-sphw

sascha-sphw

Hier würde ich aber noch erweitern, dass int generell nicht unsigned ist. Hier funktioniert das, weil int 4 bytes hat und somit das Vorzeichen Bit nicht berührt wird und der Wert somit positiv bleibt.
 
kodela

kodela

Das Problem ist, dass einerseits Bit 7 richtig abgefragt wird, was mit dem Vorschlag von httpdigest für ein Byte erreicht werden könnte, aber rechts von Bit 7 steht ja auch noch etwas, was durch die negative Interpretation des Gesamtwertes auf Grund des führend gesetzten Bits 7 sich ja auch verändert.
 
H

httpdigest

Wenn du ein Byte mit dem Bitpattern 11010011 hast und du &0xFF darauf anwendest, hast du ein int mit dem Bitpattern `00000000 00000000 00000000 11010011`. Es findet hier kein sign-extend statt.
 
mihe7

mihe7

Das Problem ist, dass einerseits Bit 7 richtig abgefragt wird, was mit dem Vorschlag von httpdigest für ein Byte erreicht werden könnte, aber rechts von Bit 7 steht ja auch noch etwas, was durch die negative Interpretation des Gesamtwertes auf Grund des führend gesetzten Bits 7 sich ja auch verändert.
Ich versteh nur Bahnhof :)
 
H

httpdigest

Ja, mein neuronales Netzwerk ist sich auch nicht sicher, welches Problem da eigentlich genau gemeint ist. Mit Confidence 49.99% ist es Problem "Ich caste nach int und dadurch bekomme ich ein sign-extend" und mit Confidence 50.01% ist es: "Ich weiss nicht genau, was das Problem ist und habe nur eine Vermutung, ob das, was ich da erzähle, eigentlich überhaupt ein Problem darstellt und frage einfach mal wirr in den Raum." :)

Deswegen probe ist jetzt mal die 49.99%-Confidence-Vermutung:

@kodela Du darfst das byte nicht explizit auf int casten bevor du die bitweise UND-Verknüpfung anwendest. Das Casten auf int führt dann zu einem sign-extend, so dass das 8. Bit IMMER 0 ist. Also einfach direkt auf dem byte & 0xFF anwenden.
 
T

temi

rechts von Bit 7 steht ja auch noch etwas, was durch die negative Interpretation des Gesamtwertes auf Grund des führend gesetzten Bits 7 sich ja auch verändert
Warum ändert sich da was, wenn du dir das Bit 7 anschaust? Hat das was mit Schrödingers Katze zu tun?
 
kodela

kodela

Nein, das hat mit Schröder Katze nichts zu tun, es hat allein mit einer falschen Beurteilung meinerseits etwas zu tun. Tut mir Leid. Der Mensch irrt, so lange er strebt. Java interpretiert den gesamten Wert negativ wenn in einem Byte das Bit 7 gesetzt ist. Wird der Rest ohne diesem gesetzten Bit 7 abgefragt, dann ist das Ergebnis positiv und auch richtig. Entschuldigt bitte meinen Denkfehler und vielen Dank für all die Hinweise.

kodela
 
mihe7

mihe7

@kodela, kein Grund, sich für irgendwas zu entschuldigen. Ich habe in #44 nur nicht verstanden, was der Satz bedeuten soll (und verstehe ihn bis jetzt nicht :))
 
kodela

kodela

Hallo,

ja, ich denke, die Lösung liegt in einer Änderung der Abfrage. Unter C hatte ich für das EB-Array den Typ Char. Dieser Typ hat eine Länge von 8 Bit, also einem Byte. In Java hat Char eine Länge von zwei Byte, weshalb ich es mit dem Typ Byte versuchte. Unter C ergibt die Abfrage auf gesetztes Bit 7 mit if ((Char-Wert & 0x80) > 0) true, wenn es gesetzt ist. Unter Java muss ich statt dessen die Abfrage so formulieren: ((Byte-Werte & 0x80) == 0x80), denn das führende Bit gibt für das signed Byte ja einen negativen Wert.

Um allerdings sicher zu sein, muss ich noch einiges überprüfen, denn dieser Byte-Wert muss eventuell für eine Bearbeitung der EB auch editiert werden. Bevor ich das alles auf Herz und Nieren überprüfen kann, muss ich mich mit einer anderen Baustelle beschäftigten, auf der auch nicht alles rund läuft.

kodela
 
mrBrown

mrBrown

Unter C ergibt die Abfrage auf gesetztes Bit 7 mit if ((Char-Wert & 0x80) > 0) true, wenn es gesetzt ist. Unter Java muss ich statt dessen die Abfrage so formulieren: ((byte-Werte & 0x80) == 0x80), denn das führende Bit gibt für das signed Byte ja einen negativen Wert.
Ich finde die Java-Variante sinnvoller und würde es auch in C so schreiben, bzw das ganze sogar direkt binär schreiben:
(b & 0b10000000) == 0b10000000
 
mihe7

mihe7

Unter Java muss ich statt dessen die Abfrage so formulieren: ((Byte-Werte & 0x80) == 0x80), denn das führende Bit gibt für das signed Byte ja einen negativen Wert.
Code:
jshell> byte b = (byte) 0xff
b ==> -1

jshell> (b & 0x80) > 0
$2 ==> true

jshell> if ((b & 0x80) > 0) System.out.println("true");
true
 
T

temi

Ich finde die Java-Variante sinnvoller und würde es auch in C so schreiben, bzw das ganze sogar direkt binär schreiben:
(b & 0b10000000) == 0b10000000
Und ich würde noch Konstanten für die Variantenabfrage einführen private final static byte VARIANTE_A = (byte)0b10000000; und (b & VARIANTE_A) == VARIANTE_A
 
Zuletzt bearbeitet:
kodela

kodela

Wau, danke für Euer Interesse!

Und, ich würde es heute in C auch anders schreiben, aber als ich vor 25 Jahren an diesem Programm arbeitete, war vieles anders, angefangen mit meinem Wissen zum Programmieren mit der für mich ersten leistungsfähigen Hochsprache, das ich mir mit diesem Programm über knapp 10 Jahre hinweg erarbeitete.

Zum Vorschlag mit den Konstanten, klar, solche verwende ich reichlich, würden hier aber nicht unbedingt mehr Klarheit bringen, da dann ja auch gezeigt werden müsste, für was zum Beispiel "VARIANTE_A " steht.

Nur noch einmal zur Erinnerung. Wie ich in meinem ersten Beitrag geschrieben habe, geht es mir nur darum, was man alles anstellen muss, um ein relativ komplexes C++ Programm mit möglichst wenig Änderungen nach Java zu übersetzen. Diese Aufgabe hat mich besonders gereizt, weil ich mich selbst wundere, wie leistungsfähig auch heute noch ein Programm aus den 90er Jahren sein kann. Wer sich dafür interessiert, kann sich bowili-Schach, so habe ich es genannt, weil die Basis dieses Programms ein Demo-Projekt zu Borlands Object Windows Library war, ja einmal ansehen. Manches funktioniert zwar nicht mehr, zum Beispiel die Hilfe-Datei, anderes ist längst überflüssig, zum Beispiel die Kalibrierung der Zeitmessung nach dem ersten Start, aber im Grunde ist das Programm heute etwa um den Faktor 100 leistungsfähiger als früher. Zur Entwicklungszeit wurden in einer Sekunde etwa 30.000 Züge überprüft. Heute sind es durchschniittlich 3 Millionen.

kodela
 
mrBrown

mrBrown

Zum Vorschlag mit den Konstanten, klar, solche verwende ich reichlich, würden hier aber nicht unbedingt mehr Klarheit bringen, da dann ja auch gezeigt werden müsste, für was zum Beispiel "VARIANTE_A " steht.
Wenn das Ding passend benannt ist, wird doch genau damit gezeigt, was gemeint ist :) VARIANTE_A ist ja hier nur ein Platzhalter für einen sinnvollen Namen.
 
mihe7

mihe7

Borlands Object Windows Library war,
LOL, das Teil hatte ich auch, ich meine Version 3.x; in einer Box mit gefühlt 25 fetten Handbüchern :)

BTW: wo man aufpassen muss in Java ist, wenn man mit dem Right Shift Operator arbeitet. Hier hat Java extra einen unsigned operator ">>>", den man tunlichst verwenden sollte, will man keine bösen Überraschungen bzgl. des Vorzeichens erleben.
 
kodela

kodela

Wenn das Ding passend benannt ist, wird doch genau damit gezeigt, was gemeint ist :) VARIANTE_A ist ja hier nur ein Platzhalter für einen sinnvollen Namen.
Ja, mir ist schon klar, was gemeint war, aber im konkreten Fall geht es doch um einen ganz bestimmten Wert für eine Verknüpfung, über die abgefragt werden soll, ob es zum aktuellen Zug, der über die ersten sechs Bits bestimmt ist, eine Fortsetzung oder eine Variante gibt. Wenn das nicht der Fall ist, dürfte von den letzten beiden Bits nur das Bit sieben gesetzt sein. Um dies im konkreten Fall zu ermitteln, sie die Abfrage so aus:
if (((openings[libNo] & 0xFF) & 0x80) == 0x80)

Man könnte natürlich für FF-Maske schlicht und einfach die Konstante ALLE und für das Bit sieben zum Beispiel END definieren. Das sähe dann so aus:
if (((openings[libNo] & ALLE) & END) == END)

Wenn ich das in meinem Programm so mache, ist das wohl ok. Wenn ich aber hier nach einer Lösung suche, wie ich für einen signierten Byte-Wert die Abfrage gestalte, dann scheint mir die erste Variante ausnahmsweise doch informativer zu sein.

das Teil hatte ich auch, ich meine Version 3.x; in einer Box mit gefühlt 25 fetten Handbüchern
Nein, 25 meist dicke Handbücher (in deutscher Sprache) waren es nicht, aber alle nebeneinander gestllt benötigten sie schon einen Meter Platz.

kodela
 
mrBrown

mrBrown

Ja, mir ist schon klar, was gemeint war, aber im konkreten Fall geht es doch um einen ganz bestimmten Wert für eine Verknüpfung, über die abgefragt werden soll, ob es zum aktuellen Zug, der über die ersten sechs Bits bestimmt ist, eine Fortsetzung oder eine Variante gibt. Wenn das nicht der Fall ist, dürfte von den letzten beiden Bits nur das Bit sieben gesetzt sein. Um dies im konkreten Fall zu ermitteln, sie die Abfrage so aus:
if (((openings[libNo] & 0xFF) & 0x80) == 0x80)

Man könnte natürlich für FF-Maske schlicht und einfach die Konstante ALLE und für das Bit sieben zum Beispiel END definieren. Das sähe dann so aus:
if (((openings[libNo] & ALLE) & END) == END)

Wenn ich das in meinem Programm so mache, ist das wohl ok. Wenn ich aber hier nach einer Lösung suche, wie ich für einen signierten Byte-Wert die Abfrage gestalte, dann scheint mir die erste Variante ausnahmsweise doch informativer zu sein.
Die Abfrage kannst du kürzen zu if ((openings[libNo] & 0x80) == 0x80), das Und ist assoziativ :)

Ehrlich gesagt seh ich das Anfangsproblem aber auch gar nicht, man kommt ja auch ohne explizites Verenden mit 0xFF an jedes Bit?

(yx & MASK) != 0 oder (yx & MASK) == MASK sind zumindest mMn dabei die einfachste Varianten, man muss halt nur einen explizites gleich oder ungleich nutzen, und kein größer/kleiner, würde ich in C nicht anders machen.
 
Thema: 

byte als unsigned interpretieren

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben