x86-64-Maschinencode, 24 Byte
6A 0A 5E 31 C9 89 F8 99 F7 F6 01 D1 85 C0 75 F7 8D 04 09 99 F7 F7 92 C3
Der obige Code definiert eine Funktion im 64-Bit-x86-Maschinencode, die bestimmt, ob der Eingabewert durch die doppelte Summe seiner Ziffern teilbar ist. Die Funktion entspricht der System V AMD64-Aufrufkonvention, sodass sie aus praktisch jeder Sprache aufgerufen werden kann, als wäre sie eine C-Funktion.
Es wird ein einzelner Parameter als Eingabe über das EDI
Register gemäß der aufrufenden Konvention verwendet, bei der es sich um die zu testende Ganzzahl handelt. (Es wird angenommen, dass dies eine positive Ganzzahl ist, die den Herausforderungsregeln entspricht und für die CDQ
Anweisung erforderlich ist, die wir verwenden, um korrekt zu arbeiten.)
Es gibt sein Ergebnis EAX
wieder gemäß der Aufrufkonvention im Register zurück. Das Ergebnis wird 0 sein , wenn der Eingangswert ist durch die Summe ihrer Ziffern teilbar und nicht-Null sonst. (Grundsätzlich ein inverser Boolescher Wert, genau wie das in den Herausforderungsregeln angegebene Beispiel.)
Sein C-Prototyp wäre:
int DivisibleByDoubleSumOfDigits(int value);
Hier sind die Anweisungen in Assemblersprache ohne Golf, die mit einer kurzen Erläuterung des Zwecks der einzelnen Anweisungen versehen sind:
; EDI == input value
DivisibleByDoubleSumOfDigits:
push 10
pop rsi ; ESI <= 10
xor ecx, ecx ; ECX <= 0
mov eax, edi ; EAX <= EDI (make copy of input)
SumDigits:
cdq ; EDX <= 0
div esi ; EDX:EAX / 10
add ecx, edx ; ECX += remainder (EDX)
test eax, eax
jnz SumDigits ; loop while EAX != 0
lea eax, [rcx+rcx] ; EAX <= (ECX * 2)
cdq ; EDX <= 0
div edi ; EDX:EAX / input
xchg edx, eax ; put remainder (EDX) in EAX
ret ; return, with result in EAX
Im ersten Block führen wir eine vorläufige Initialisierung der Register durch:
PUSH
+ POP
-Anweisungen werden als langsamer, aber kurzer Weg zur Initialisierung ESI
auf 10 verwendet. Dies ist erforderlich, da die DIV
Anweisung auf x86 einen Registeroperanden erfordert. (Es gibt keine Form, die durch einen unmittelbaren Wert von beispielsweise 10 dividiert.)
XOR
wird als kurzer und schneller Weg zum Löschen des ECX
Registers verwendet. Dieses Register dient als "Akkumulator" innerhalb der anstehenden Schleife.
- Schließlich wird eine Kopie des Eingabewerts (von
EDI
) erstellt und in gespeichert EAX
, die beim Durchlaufen der Schleife gelöscht wird.
Dann beginnen wir, die Ziffern im Eingabewert zu schleifen und zu summieren. Dies basiert auf dem x86- DIV
Befehl, der EDX:EAX
durch seinen Operanden dividiert und den Quotienten in EAX
und den Rest in zurückgibt EDX
. Was wir hier tun, ist, den Eingabewert durch 10 zu teilen, so dass der Rest die Ziffer an der letzten Stelle ist (die wir unserem Akkumulatorregister hinzufügen ECX
) und der Quotient die verbleibenden Ziffern sind.
- Der
CDQ
Befehl ist eine kurze Methode, um EDX
auf 0 zu setzen. Er erweitert den Wert tatsächlich EAX
auf EDX:EAX
, was DIV
als Dividende verwendet wird. Wir brauchen hier eigentlich keine Vorzeichenerweiterung, da der Eingabewert nicht vorzeichenbehaftet ist, sondern CDQ
1 Byte beträgt, im Gegensatz zur Verwendung von XOR
to clear EDX
, was 2 Byte entsprechen würde.
- Dann haben wir
DIV
ide EDX:EAX
durch ESI
(10).
- Der Rest (
EDX
) wird zum Akku ( ECX
) hinzugefügt .
- Das
EAX
Register (der Quotient) wird getestet, um festzustellen, ob es gleich 0 ist. Wenn ja, haben wir alle Ziffern durchlaufen und fallen durch. Wenn nicht, haben wir noch mehr Ziffern zu summieren, und kehren zum oberen Ende der Schleife zurück.
Nachdem die Schleife beendet ist, implementieren wir number % ((sum_of_digits)*2)
:
Der LEA
Befehl wird verwendet, um ECX
mit 2 zu multiplizieren (oder ECX
sich selbst zu addieren ) und das Ergebnis in einem anderen Register zu speichern (in diesem Fall EAX
).
(Wir hätten auch add ecx, ecx
+ machen können xchg ecx, eax
; beide sind 3 Bytes, aber die LEA
Anweisung ist schneller und typischer.)
- Dann
CDQ
bereiten wir uns erneut auf die Teilung vor. Da EAX
positiv (dh ohne Vorzeichen) ist, bewirkt dies EDX
wie zuvor eine Nullsetzung .
- Als nächstes folgt die Division, diesmal dividiert
EDX:EAX
durch den Eingabewert (eine ungestörte Kopie davon befindet sich noch in EDI
). Dies ist äquivalent zu Modulo, mit dem Rest in EDX
. (Der Quotient wird ebenfalls eingegeben EAX
, aber wir brauchen ihn nicht.)
- Schließlich
XCHG
tauschen wir die Inhalte von EAX
und aus EDX
. Normalerweise würden Sie eine MOV
hier machen, aber XCHG
ist nur 1 Byte (wenn auch langsamer). Da EDX
der Rest nach der Division enthalten ist, ist er 0, wenn der Wert gleichmäßig teilbar war, oder ansonsten ungleich Null. Wenn wir also RET
urnieren, ist EAX
(das Ergebnis) 0, wenn der Eingabewert durch die doppelte Summe seiner Ziffern teilbar war, oder andernfalls ungleich Null.
Hoffentlich reicht das für eine Erklärung.
Dies ist nicht der kürzeste Eintrag, aber hey, es sieht so aus, als ob er fast alle Nicht-Golf-Sprachen übertrifft! :-)