Ist das LSB gesetzt?
Einleitung
Im letzten Beitrag haben wir uns mit dem MSB bzw wertniedrigsten Bit beschäftigt, hier soll es um seinen Konterpart, das LSB oder werthöchste Bit gehen. Je nachdem ob wir einen Typ mit oder ohne Vorzeichen verwenden zeigt uns dieses Bit das Vorzeichen an bzw teilt uns mit das der Wert sehr gross, in Bezug zum Datentyp, ist.
Vorüberlegungen
Im Gegensatz zum letzten Artikel ergibt sich ein Problem: Durch die verschiedenen Bitbreiten der Typen liegt das interessante Bit immer woanders:
| Typen | Bitposition |
|---|---|
| (u)byte | 7 |
| (u)short | 15 |
| (u)int | 31 |
| (u)long | 63 |
Angestrebt ist eine Lösung die für alle Datentypen gleichermassen funktioniert. Wie wir aus dem vorherigen Beitrag wissen, können wir mit der bitweisen Und-Verknüpfung prüfen, ob ein spezifisches Bit gesetzt ist oder eben nicht. Nachteilig auf den ersten Blick, wirken die variablen Positionen unseres Zielbits. Ein Wert muss sich zum anderen hin bewegen, dafür gibt gibt es die sogenannten Shift-Operatoren << und >>, welchen den linken Operanden um die rechts gegebene Anzahl Bits in Richtung der Pfeilspitzen verschieben. Die Wirkung hängt in der ersten Linie davon ab, wie stark die verwendete Sprache von der Hardware abstrahiert, dazu an andere Stelle mehr. Das Referenzhandbuch für D1 cmacht keine Angaben dazu wie sich die Shiftoperatoren verhalte, das obliegt wohl dem verwendeten Compiler.
Um es kurz zu halten, eine Variable zu sparen und den finalen Ausdruck im return-Statement nicht zu zu sehr in die Länge zu ziehe, werden wir im Folgenden useren Prüfling nach rechts schieben, bis das original LSB an der Position 0 steht und wir wie gewohnt das MSB checken können.
Implementierung
Was wollen wir erhalten?
Zuerst wieder die Frage nach den gewünschten Resultaten. Es soll ein boolzurückgegeben werden, welches mitteilt ob das LSB gesetzt ist, da wir die komplette Pallete an Integertypen abdecken wollen, ist das Template das Mittel der wahl, mehr dazu in Kürze:
unittest
{
import std.stdio: writeln, write;
writeln("Testing lsbtest:");
write("byte: 14 (expecting: false): ");
assert(false == LSBset!(byte).check(14));
write("passed\n ubyte: 254 (expexting: true): ");
assert(true == LSBset!(ubyte).check(254));
write("passed\n int: -2 (expected: true): ");
assert(true == LSBset!(int).check(-2));
write("passed\n long: 2000000 (expected: false): ");
assert(false == LSBset!(long).check(200000));
writeln("passed");
writeln("All tests (4) passed.");
}Das Template
Die angestrebte generische Lösungsvariante wird in D durch Templates2 realisiert. Grob gesagt handelt es dabei um Container für Funktionen und Variablen, die eine spezielle Syntax für Platzhalter zulassen, wo normalerweise ein wohldefinierter Typbezeicher stünde. Sollen jetzt Routinen mit einer Gruppe verwandter Datentypen, also solchen mit identischen Eigenschaften, arbeiten – wie hier mit Ganzzahltypen –, müssen wir jene nur schblonieren und, wie in den Unittests gezeigt, entsprechend initialiesieren. Im Falle unserer Überlegungen sähe, dass dann exemplarisch so aus:
template LSBset(T)
{
bool check(T candidate)
{
candidate = candidate >> ((T.sizeof*8) -1);
return (1 == (candidate & 1));
}
}
// es folgen obenstehende TestsWie der Dokumentation zu entnehmen ist gibt es in D zwei Varianten Bits nach rechts (kleiner werdende Richtung) zu schieben.
a) unter Beibehaltung des Vorzeichens (>>)
b) ohne Beachtung des Vorzeichens (>>>)
Da alle Ganzzahltypen unterstützt werden sollen, bleibt uns nur ersterer, ansonsten ernten wir Fehler während der Übersetzung. Gründe hierfür sind nicht unterstützte implizite Typumwandlungen (soganannte Casts) von Vorzeichen behafteten Typen (byte und int) in vorzeichenlose Typen.
Ebenfalls als neues Syntaxelement shen wir die Eigenschaft3 sizeof, die jedem Datentyp – damit auch jeder Variablen – begegeben ist Anzahl der belegten Bytes liefert. Um die Grösse in Bit zu erhalten, multiplizieren wir mit acht. Damit das zu prüfende Bit nicht aus dem Speicher hinausgeschoben wird, subtrahieren wir eins, weil n — 1 Shiftings das LSB auf die Position des MSB bringen.
Zum Abschluss noch der MSB-Test, wie wir ihn bereits kennen.
Weiter oben sprach ich von kryptischen Ausdrücken, welche entstehen können, wenn man bestrebt alles in kompaktester Weise zu notieren. Als Beispiel bringe ich obige Variante als Einzeiler, die beim Debuggen keine Freude macht, wenn der compiler einen Fehler wirft:
return (1 ==((candidate >> ((candidate.sizeof*8) -1)) & 1));Fazit
Templating in der hier gezeigten Form ist sehr elegant, da man keine komplette Klasse konstruieren muss um generischen Code zu bauen, hier der funktional Identische Code in Pascal
{$mode objfpc}
program lsbset;
type
generic TLsbSet<_T> = class(TObject)
public
function check(candidate : _T) : boolean;
end;
TIsIntegerNegative = specialize TLsbSet<integer>;
function TLsbSet.check(candidate: _T) : boolean;
begin
{$ifdef ENDIAN_LITTLE}
candidate := candidate shr ((sizeOf(candidate)*8)-1);
{$else}
candidate: = candidate shl ((sizeOf(candidate)*8)-1);
{$endif}
check := (1 = candidate and 1);
end;
var
negint : TIsIntegerNegative;
Begin
negint := TIsIntegerNegative.create;
writeln('Is -12 negative: ', negint.check(-12));
writeln('Is 1024 negative: ', negint.check(1024));
End.Zieht man das Hauptprogramm ab, weil ich in D den vom DMD bereitgestellten Default-Stub (Schalter -main) für den Testrunner nutze, erkennt der/die geneigte Lesende sehr deutlich den Code-Überhang, der durch das Definieren und Implemtieren der Klasse entsteht. Zusätzlich sieht man, dass der Code Rücksicht auf die Endianess der Zielarchitektur nemen muss, da auf Big-Endian-Systemen nach links zu schieben ist, D abstrahiert diesen Schritt für den Programmierer dankenswerter Weise.
Kehrseite der Medalie ist allerdings, dass man in D nicht auf triviale Weise in die oberen 32 Bit eines long-Wertes hineinschieben kann, da es bei Linksshifts eine Beschränkung auf höchstens 31 Schritte gibt. Das MSB zu setzen geht dann zwar, aber man kommt mit seiner 1 dann nicht auf konsitente Art zu Bit 63. Dies ist auch einer der Gründe, warum ich diese Variante hier nicht vorgestellt habe.
Referenzen
-
https://dlang.org/spec/expression.html#shift_expressions (zuletzt aufgerufen 2026/02/22) ↩
-
https://dlang.org/spec/template.html (zuletzt aufgerufen 2026/02/24) ↩
-
https://dlang.org/spec/property.html (zuletzt aufgerufen 2026/02/25) ↩