Código da máquina x86-64, 24 bytes
6A 0A 5E 31 C9 89 F8 99 F7 F6 01 D1 85 C0 75 F7 8D 04 09 99 F7 F7 92 C3
O código acima define uma função no código de máquina x86 de 64 bits que determina se o valor de entrada é divisível pelo dobro da soma de seus dígitos. A função está em conformidade com a convenção de chamada do System V AMD64, para que possa ser chamada de praticamente qualquer idioma, como se fosse uma função C.
Ele usa um único parâmetro como entrada via EDI
registrador, conforme a convenção de chamada, que é o número inteiro a ser testado. (Supõe-se que seja um número inteiro positivo , consistente com as regras de desafio e é necessário para que as CDQ
instruções que usamos funcionem corretamente.)
Retorna seu resultado no EAX
registro, novamente, conforme a convenção de chamada. O resultado será 0 se o valor de entrada for divisível pela soma de seus dígitos e diferente de zero. (Basicamente, um booleano inverso, exatamente como o exemplo dado nas regras de desafio.)
Seu protótipo C seria:
int DivisibleByDoubleSumOfDigits(int value);
Aqui estão as instruções da linguagem assembly não destruídas, anotadas com uma breve explicação do objetivo de cada instrução:
; 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
No primeiro bloco, fazemos algumas inicializações preliminares dos registros:
PUSH
As POP
instruções + são usadas como uma maneira lenta, mas curta, de inicializar ESI
para 10. Isso é necessário porque a DIV
instrução em x86 requer um operando de registro. (Não existe uma forma que divida por um valor imediato de, digamos, 10.)
XOR
é usado como uma maneira curta e rápida de limpar o ECX
registro. Este registro servirá como o "acumulador" dentro do próximo loop.
- Finalmente, uma cópia do valor de entrada (de
EDI
) é feita e armazenada EAX
, que será eliminada à medida que avançamos no loop.
Então, começamos a repetir e somar os dígitos no valor de entrada. Isso se baseia na DIV
instrução x86 , que divide EDX:EAX
por seu operando e retorna o quociente in EAX
e o restante in EDX
. O que faremos aqui é dividir o valor de entrada por 10, de modo que o restante seja o dígito em último lugar (que adicionaremos ao nosso registro acumulador ECX
), e o quociente é o restante.
- A
CDQ
instrução é uma maneira curta de definir EDX
como 0. Na verdade , ela estende o valor EAX
para EDX:EAX
, que é o que DIV
usa como dividendo. Na verdade, não precisamos de extensão de sinal aqui, porque o valor de entrada não é assinado, mas CDQ
é de 1 byte, em vez de usar XOR
para limpar EDX
, que seria de 2 bytes.
- Então
DIV
ide EDX:EAX
por ESI
(10).
- O restante (
EDX
) é adicionado ao acumulador ( ECX
).
- O
EAX
registro (quociente) é testado para verificar se é igual a 0. Nesse caso, passamos por todos os dígitos e seguimos em frente. Caso contrário, ainda temos mais dígitos para somar, então voltamos ao topo do loop.
Por fim, após a conclusão do loop, implementamos number % ((sum_of_digits)*2)
:
A LEA
instrução é usada como uma maneira curta de multiplicar ECX
por 2 (ou, equivalentemente, adicionar ECX
a si mesma) e armazenar o resultado em um registro diferente (neste caso EAX
).
(Também poderíamos ter feito add ecx, ecx
+ xchg ecx, eax
; ambos são 3 bytes, mas a LEA
instrução é mais rápida e mais típica.)
- Então, fazemos
CDQ
novamente para nos preparar para a divisão. Como EAX
será positivo (isto é, não assinado), isso tem o efeito de zerar EDX
, exatamente como antes.
- A seguir, é a divisão, desta vez dividida
EDX:EAX
pelo valor de entrada (uma cópia não-modificada da qual ainda reside EDI
). Isso é equivalente ao módulo, com o restante em EDX
. (O quociente também é inserido EAX
, mas não precisamos dele.)
- Finalmente,
XCHG
trocamos o conteúdo de EAX
e EDX
. Normalmente, você faria um MOV
aqui, mas XCHG
é apenas 1 byte (embora mais lento). Como EDX
contém o restante após a divisão, será 0 se o valor for igualmente divisível ou diferente de zero. Assim, quando RET
urn, EAX
(o resultado) será 0 se o valor de entrada for divisível pelo dobro da soma de seus dígitos, ou diferente de zero.
Espero que isso seja suficiente para uma explicação.
Esta não é a entrada mais curta, mas, ei, parece que bate quase todas as línguas que não são de golfe! :-)