Ungerade oder nicht?
Einleitung
Wenn man von einer anderen Programmiersorache kommt – in meinem Fall Pascal — und sich an neue heran wagt, sucht man seine vertrauten Helfelein.
Ich hantiere gerne mit Ganzzahlen, um Beispielsweise Primzahllisten zu ereugen. Eine Hauptfrage die sich in diesem Kontext stellt, ist ob ein Kandidat gerade oder ungerade ist. In Pascal greift der findige Programmierer zum Funktion odd() aus der Unit System, welche die gewünschte Information in eine boolean-Wertes liefert. Die Sprache D kennt eine solche Hilfsroutine out of the Box nicht, zumindest hab ich die in der Standardbibliothek bislang nicht gefunden. Als kleine Fingerübung implentieren wir ein solche im Nachgang.
Vorüberlegungen
Es gibt zwei Herangehensweisen an dieses Thema:
a) eine arithmetische, über den Modulo-Operator a%b
b) eine logische über bitweise Verknüpfungen.
Variante a) ist aus meiner Sicht langweilig, weil zu oft verwendet. Da die gängigen Prozessoren der x86/x64 keine Assembler-Instruktion für die Modulo-Rechnung haben, ist diese Variante von den Laufzeitkosten her recht teuer, da sie aus mehreren Opcodes zusammengebaut werden muss. Bei Variante b) ist nur zu Prüfen ob im Kandidaten das wertniederste Bit – engl. most significant bit (MSB) – gesetzt ist, da dieses den Wert 1 repräsentiert. Dankenswerter Weise müssen wir hier keine besondere Rücksich auf die Speicherrepräsentation des Kandidaten nehmen. Der größte Vorteil liegt in der Tatsache, dass alle moderenen Prozessorfamilien bitweise Logikoperatoren in ihren Assemblern haben und in diesen sehr effizient sind.
Implementierung
Was wollen wir erhalten?
Die Sprache D hat ein interessantes Feature, eingebaute Unittests1, deshalb überlegen wir zuerst welche Ergebnisse wir erhalten wollen. Die Funktion soll ein bool liefern, derart dass sie true zurückgibt, wenn die Zahl ungerade ist, ansonsten false, die Tests sehen dann zum Beispiel so aus:
unittest
{
import std.stdio: write, writeln;
writeln("testing odd()");
write("testing with even argument (expecting: false): ");
assert(false == odd(4));
write("pass\ntesting with odd value (expecting: true): ");
assert(true == odd(3));
writeln("pass\n odd() passed all tests (2)!");
}Die Funktion
Die von mir bevorzugte Variante ist b). Da geprüft werden soll ob ein spezifisches Bit gesetzt ist, wählen wir die und-Verknüpfung, da diese folgende Eigenschaften hat:
| & | 0 | 1 |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 0 | 1 |
Da wir keine Zwischenschritte haben können wir die Rechnung direkt in der return-Anweisung vornehemen, womit der Quelcode dann folgende Form annimmt:
@safe @nogc nothrow pure
bool odd(immutable ulong n)
{
return (1 == (n & 1));
}
// hier folgen die UnittestsDMD compiliert oben stehendes zu nach stehenden Assembler Zeilen:
and RDI,1
mov EAX,1
cmp RDI,RAX
setz AL
retVariante a)
Der Vollständigkeit halber hier noch D-Code und Assembler für Variante a):
@safe @nogc nothrow pure
bool mododd(immutable ulong n)
{
return (1 == (n % 2));
}Wie schaut nun der Assembler-Code aus? Erwartet hatte ich etwas in folgender Form:
mov RAX,RDI
mov ECX,2
xor EDX,EDX
div RCX
cmp RDX,1
setz AL
retWobei mir nicht klar ist, warum er das EDX-Register löscht in dem er es mir mit sich selbst »exklusiv odert«?
Da moderne Compiler »mitdenken« und optimieren sehen wir einen alten bekannten wieder:
and RDI,1
mov EAX,1
cmp RDI,RAX
setz AL
retDer Compiler substituiert das Modulo durch den MSB-Set-Test.
Fazit
Es ist ineteressant, Dinge mal anders zu machen, um Verständnis für Abläufe zu entwickeln über welche man sich kaum mehr Gedanken macht. Augenscheinlich ist dies wegen der höheren Kerntakte und schieren Speichermengen heute auch kaum mehr notwendig, aber je nach Platform ist händische Voroptimierung durchaus sinnvoll, weil moderne Compiler dann nicht die Nachlässigkeiten von Programmieren oder Codegeneratoren ausbügeln müssen, sondern kleinteiliger optimieren können.
Changelog
2026-03-09: Referenzen hinzugefügt. 2026-03-15: Umstelung Tabelle auf HTML, da jekyll keine MArkdown-Table-Syntax unterstützt.
Referenzen
-
https://dlang.org/spec/unittest.html “(2026-03-09)” ↩