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 EDIRegister 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 CDQAnweisung erforderlich ist, die wir verwenden, um korrekt zu arbeiten.)
Es gibt sein Ergebnis EAXwieder 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 ESIauf 10 verwendet. Dies ist erforderlich, da die DIVAnweisung auf x86 einen Registeroperanden erfordert. (Es gibt keine Form, die durch einen unmittelbaren Wert von beispielsweise 10 dividiert.)
XORwird als kurzer und schneller Weg zum Löschen des ECXRegisters 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- DIVBefehl, der EDX:EAXdurch seinen Operanden dividiert und den Quotienten in EAXund 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
CDQBefehl ist eine kurze Methode, um EDXauf 0 zu setzen. Er erweitert den Wert tatsächlich EAXauf EDX:EAX, was DIVals Dividende verwendet wird. Wir brauchen hier eigentlich keine Vorzeichenerweiterung, da der Eingabewert nicht vorzeichenbehaftet ist, sondern CDQ1 Byte beträgt, im Gegensatz zur Verwendung von XORto clear EDX, was 2 Byte entsprechen würde.
- Dann haben wir
DIVide EDX:EAXdurch ESI(10).
- Der Rest (
EDX) wird zum Akku ( ECX) hinzugefügt .
- Das
EAXRegister (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 LEABefehl wird verwendet, um ECXmit 2 zu multiplizieren (oder ECXsich 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 LEAAnweisung ist schneller und typischer.)
- Dann
CDQbereiten wir uns erneut auf die Teilung vor. Da EAXpositiv (dh ohne Vorzeichen) ist, bewirkt dies EDXwie zuvor eine Nullsetzung .
- Als nächstes folgt die Division, diesmal dividiert
EDX:EAXdurch 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
XCHGtauschen wir die Inhalte von EAXund aus EDX. Normalerweise würden Sie eine MOVhier machen, aber XCHGist nur 1 Byte (wenn auch langsamer). Da EDXder Rest nach der Division enthalten ist, ist er 0, wenn der Wert gleichmäßig teilbar war, oder ansonsten ungleich Null. Wenn wir also RETurnieren, 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! :-)