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 EDIregistrador, 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 CDQinstruções que usamos funcionem corretamente.)
Retorna seu resultado no EAXregistro, 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:
PUSHAs POPinstruções + são usadas como uma maneira lenta, mas curta, de inicializar ESIpara 10. Isso é necessário porque a DIVinstruçã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 ECXregistro. 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 DIVinstrução x86 , que divide EDX:EAXpor seu operando e retorna o quociente in EAXe 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
CDQinstrução é uma maneira curta de definir EDXcomo 0. Na verdade , ela estende o valor EAXpara EDX:EAX, que é o que DIVusa 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 XORpara limpar EDX, que seria de 2 bytes.
- Então
DIVide EDX:EAXpor ESI(10).
- O restante (
EDX) é adicionado ao acumulador ( ECX).
- O
EAXregistro (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 LEAinstrução é usada como uma maneira curta de multiplicar ECXpor 2 (ou, equivalentemente, adicionar ECXa 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 LEAinstrução é mais rápida e mais típica.)
- Então, fazemos
CDQnovamente para nos preparar para a divisão. Como EAXserá positivo (isto é, não assinado), isso tem o efeito de zerar EDX, exatamente como antes.
- A seguir, é a divisão, desta vez dividida
EDX:EAXpelo 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,
XCHGtrocamos o conteúdo de EAXe EDX. Normalmente, você faria um MOVaqui, mas XCHGé apenas 1 byte (embora mais lento). Como EDXcontém o restante após a divisão, será 0 se o valor for igualmente divisível ou diferente de zero. Assim, quando RETurn, 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! :-)