x86-64 Codice macchina, 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
Il codice precedente definisce una funzione nel codice macchina x86 a 64 bit che determina se il valore di input è divisibile per il doppio della somma delle sue cifre. La funzione è conforme alla convenzione di chiamata AMD64 di System V, quindi è richiamabile praticamente da qualsiasi linguaggio, proprio come se fosse una funzione C.
Prende un singolo parametro come input tramite il EDIregistro, secondo la convenzione di chiamata, che è l'intero da testare. (Si presume che sia un numero intero positivo , coerente con le regole della sfida, ed è richiesto per ilCDQ corretto funzionamento istruzioni che utilizziamo.)
Restituisce il suo risultato nel EAXregistro, di nuovo, secondo la convenzione di chiamata. Il risultato sarà 0 se il valore di input era divisibile per la somma delle sue cifre e diverso da zero in caso contrario. (Fondamentalmente, un booleano inverso, esattamente come nell'esempio fornito nelle regole della sfida.)
Il suo prototipo C sarebbe:
int DivisibleByDoubleSumOfDigits(int value);
Ecco le istruzioni per il linguaggio dell'assemblea non golfate, annotate con una breve spiegazione dello scopo di ciascuna istruzione:
; 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
Nel primo blocco, eseguiamo un'inizializzazione preliminare dei registri:
PUSHLe POPistruzioni + sono usate come un modo lento ma breve per inizializzare ESIa 10. Ciò è necessario perché l' DIVistruzione su x86 richiede un operando di registro. (Non esiste una forma che si divide per un valore immediato, diciamo, di 10.)
XORè usato come un modo breve e veloce per cancellare il ECXregistro. Questo registro fungerà da "accumulatore" all'interno del ciclo imminente.
- Infine,
EDIviene creata e memorizzata una copia del valore di input (from ) EAX, che verrà bloccato durante il ciclo.
Quindi, iniziamo il ciclo e sommando le cifre nel valore di input. Questo si basa sull'istruzione x86 DIV, che si divide EDX:EAXper il suo operando e restituisce il quoziente in EAXe il resto in EDX. Quello che faremo qui è dividere il valore di input per 10, in modo tale che il resto sia la cifra nell'ultimo posto (che aggiungeremo al nostro registro accumulatore ECX), e il quoziente sono le cifre rimanenti.
- L'
CDQistruzione è un breve modo di impostare EDXa 0. E 'in realtà firmare-estende il valore di EAXa EDX:EAX, che è quello che DIVutilizza come dividendo. In realtà non abbiamo bisogno di estensione del segno qui, perché il valore di input non è firmato, ma CDQè 1 byte, invece di utilizzare XORper cancellare EDX, che sarebbe 2 byte.
- Poi abbiamo
DIVIDE EDX:EAXda ESI(10).
- Il resto (
EDX) viene aggiunto all'accumulatore ( ECX).
- Il
EAXregistro (il quoziente) viene testato per vedere se è uguale a 0. In tal caso, abbiamo superato tutte le cifre e cadiamo. In caso contrario, abbiamo ancora più cifre da sommare, quindi torniamo all'inizio del ciclo.
Infine, al termine del ciclo, implementiamo number % ((sum_of_digits)*2):
L' LEAistruzione viene utilizzata come un modo breve per moltiplicare ECXper 2 (o, equivalentemente, aggiungere ECXa se stesso) e memorizzare il risultato in un registro diverso (in questo caso, EAX).
(Avremmo anche potuto fare add ecx, ecx+ xchg ecx, eax; entrambi sono 3 byte, ma l' LEAistruzione è più veloce e più tipica.)
- Quindi, facciamo di
CDQnuovo un preparativo per la divisione. Perché EAXsarà positivo (cioè senza segno), questo ha l'effetto di azzerare EDX, proprio come prima.
- La prossima è la divisione, questa volta che si divide
EDX:EAXper il valore di input (una cui copia non rimossa risiede ancora EDI). Questo è equivalente a modulo, con il resto in EDX. (Viene inserito anche il quoziente EAX, ma non ne abbiamo bisogno.)
- Infine,
XCHGscambiamo i contenuti di EAXe EDX. Normalmente, faresti un MOVqui, ma XCHGè solo 1 byte (anche se più lento). PerchéEDX contiene il resto dopo la divisione, sarà 0 se il valore fosse equamente divisibile o diverso da zero in caso contrario. Pertanto, quando RETurliamo, EAX(il risultato) è 0 se il valore di input era divisibile per il doppio della somma delle sue cifre, o diverso da zero altrimenti.
Spero che questo sia sufficiente per una spiegazione.
Questa non è la voce più breve, ma ehi, sembra che batte quasi tutte le lingue non golf! :-)