Code machine x86-64, 24 octets
6A 0A 5E 31 C9 89 F8 99 F7 F6 01 D1 85 C0 75 F7 8D 04 09 99 F7 F7 92 C3
Le code ci-dessus dĂ©finit une fonction dans le code machine x86 64 bits qui dĂ©termine si la valeur d'entrĂ©e est divisible par le double de la somme de ses chiffres. La fonction est conforme Ă la convention d'appel System V AMD64, de sorte qu'elle peut ĂȘtre appelĂ©e Ă partir de pratiquement n'importe quel langage, comme s'il s'agissait d'une fonction C.
Il faut un seul paramĂštre en entrĂ©e via le EDIregistre, conformĂ©ment Ă la convention d'appel, qui est le nombre entier Ă tester. (Ceci est supposĂ© ĂȘtre un entier positif , compatible avec les rĂšgles de challenge, et est nĂ©cessaire au bon fonctionnement de l' CDQinstruction utilisĂ©e.)
Il retourne son résultat dans le EAX registre, encore une fois, conformément à la convention d'appel. Le résultat sera 0 si la valeur en entrée était divisible par la somme de ses chiffres et non nul sinon. (Fondamentalement, un booléen inverse, exactement comme dans les rÚgles de challenge.)
Son prototype C serait:
int DivisibleByDoubleSumOfDigits(int value);
Voici les instructions non assemblées du langage d'assemblage, annotées avec une brÚve explication du but de chaque instruction:
; 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
Dans le premier bloc, nous effectuons une initialisation préliminaire des registres:
PUSH+ les POPinstructions sont utilisées comme moyen lent mais court d'initialisationESI à 10. Cela est nécessaire car l' DIVinstruction sur x86 nécessite un opérande de registre. (Aucune forme ne divise par une valeur immédiate, disons 10.)
XOR est utilisĂ© comme un moyen rapide et rapide pour effacer le ECX registre. Ce registre servira dâaccumulateur Ă lâintĂ©rieur de la prochaine boucle.
- Enfin, une copie de la valeur dâentrĂ©e (de
EDI) est créée et stockée dansEAX laquelle elle sera compressée au fur et à mesure de notre progression dans la boucle.
Ensuite, nous commençons Ă boucler et Ă additionner les chiffres de la valeur dâentrĂ©e. Ceci est basĂ© sur lâ DIVinstruction x86 , qui divise EDX:EAXpar son opĂ©rande et renvoie le quotient dans EAXet le reste dans EDX. Ce que nous ferons ici est de diviser la valeur dâentrĂ©e par 10, de sorte que le reste soit le chiffre de la derniĂšre position (que nous ajouterons Ă notre registre accumulateur,ECX ) et le quotient, les chiffres restants.
- Lâ
CDQinstruction est un moyen simple de dĂ©finir la valeur EDXsur 0. En fait, elle signe-Ă©tend la valeur EAXĂ EDX:EAX, qui correspond DIVau dividende. En fait, nous nâavons pas besoin de lâextension de signe ici, car la valeur en entrĂ©e est non signĂ©e, mais CDQvaut 1 octet, par opposition Ă XOR de clear EDX, qui serait de 2 octets.
- Ensuite , nous
DIVIde EDX:EAXparESI (10).
- Le reste (
EDX) est ajouté à l'accumulateur ( ECX).
- Le
EAXregistre (le quotient) est testé pour voir s'il est égal à 0. Si c'est le cas, nous l'avons fait à travers tous les chiffres et nous échouons. Sinon, nous avons toujours plus de chiffres à additionner, alors nous retournons au début de la boucle.
Enfin, une fois la boucle terminée, nous implémentons number % ((sum_of_digits)*2):
L' LEAinstruction est utilisĂ©e comme un moyen rapide de multiplier ECXpar 2 (ou, de maniĂšre Ă©quivalente, de s'ajouter ECXĂ elle-mĂȘme) et de stocker le rĂ©sultat dans un registre diffĂ©rent (dans ce cas, EAX).
(Nous aurions aussi pu faire add ecx, ecx+ xchg ecx, eax; les deux sont de 3 octets, mais l' LEAinstruction est plus rapide et plus typique.)
- Ensuite, nous faisons Ă
CDQnouveau une préparation pour la division. Parce que EAXsera positif (c'est-à -dire non signé), cela a pour effet de réduire à zéro EDX, comme avant.
- Vient ensuite la division, divisée cette fois
EDX:EAXpar la valeur d'entrée (une copie non altérée de celle-ci réside toujours dans EDI). Cela équivaut à modulo, le reste étant en EDX. (Le quotient est également ajouté EAX, mais nous n'en avons pas besoin.)
- Enfin, nous
XCHGĂ©changeons le contenu de EAXet EDX. Normalement, vous feriez MOVici, mais ce XCHGnâest quâun octet (bien que plus lent). Parce que EDXcontient le reste aprĂšs la division, il sera 0 si la valeur est divisible de façon Ă©gale ou non nulle sinon. Ainsi, lorsque nous RETurn, EAX(le rĂ©sultat) est Ă©gal Ă 0 si la valeur entrĂ©e est divisible par le double de la somme de ses chiffres, ou diffĂ©rente de zĂ©ro dans le cas contraire.
J'espĂšre que cela suffit pour une explication.
Ce n'est pas l'entrée la plus courte, mais bon, on dirait qu'elle bat presque toutes les langues autres que le golf! :-)