Código de 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
El código anterior define una función en el código de máquina x86 de 64 bits que determina si el valor de entrada es divisible por el doble de la suma de sus dígitos. La función se ajusta a la convención de llamadas System V AMD64, por lo que se puede llamar desde prácticamente cualquier lenguaje, como si fuera una función C.
Toma un solo parámetro como entrada a través del EDIregistro, según la convención de llamada, que es el número entero para probar. (Se supone que este es un número entero positivo , consistente con las reglas de desafío, y se requiere para que la CDQinstrucción que usemos funcione correctamente).
Devuelve su resultado en el EAXregistro, nuevamente, según la convención de llamada. El resultado será 0 si el valor de entrada fue divisible por la suma de sus dígitos, y de lo contrario no será cero. (Básicamente, un booleano inverso, exactamente como el ejemplo dado en las reglas de desafío).
Su prototipo C sería:
int DivisibleByDoubleSumOfDigits(int value);
Aquí están las instrucciones de lenguaje ensamblador no escritas, anotadas con una breve explicación del propósito de cada instrucción:
; 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
En el primer bloque, hacemos una inicialización preliminar de registros:
PUSHLas POPinstrucciones + se usan como una forma lenta pero corta de inicializar ESIa 10. Esto es necesario porque la DIVinstrucción en x86 requiere un operando de registro. (No existe una forma que se divida por un valor inmediato de, digamos, 10.)
XORse usa como una forma corta y rápida de borrar el ECXregistro. Este registro servirá como el "acumulador" dentro del próximo ciclo.
- Finalmente, se hace una copia del valor de entrada (desde
EDI) y se almacena en ella EAX, que se registrará a medida que avancemos por el bucle.
Luego, comenzamos a recorrer y sumar los dígitos en el valor de entrada. Esto se basa en la DIVinstrucción x86 , que se divide EDX:EAXpor su operando, y devuelve el cociente EAXy el resto en EDX. Lo que haremos aquí es dividir el valor de entrada por 10, de modo que el resto sea el dígito en el último lugar (que agregaremos a nuestro registro de acumulador ECX), y el cociente son los dígitos restantes.
- La
CDQinstrucción es una forma corta de establecer EDXa 0. En realidad, firma-extiende el valor EAXa EDX:EAX, que es lo que se DIVusa como dividendo. En realidad, no necesitamos la extensión de signo aquí, porque el valor de entrada no está firmado, pero CDQes de 1 byte, en lugar de usar XORpara borrarEDX , que sería de 2 bytes.
- Entonces
DIVide EDX:EAXpor ESI(10).
- El resto (
EDX) se agrega al acumulador (ECX ).
- El
EAXregistro (el cociente) se prueba para ver si es igual a 0. Si es así, hemos superado todos los dígitos y hemos caído. Si no, todavía tenemos más dígitos para sumar, así que volvemos a la parte superior del ciclo.
Finalmente, una vez finalizado el ciclo, implementamos number % ((sum_of_digits)*2):
La LEAinstrucción se utiliza como una forma corta de multiplicar ECXpor 2 (o, equivalentemente, sumar ECXa sí mismo) y almacenar el resultado en un registro diferente (en este caso EAX).
(También podríamos haber hecho add ecx, ecx+ xchg ecx, eax; ambos son 3 bytes, pero la LEAinstrucción es más rápida y más típica).
- Luego, hacemos otra
CDQvez para prepararnos para la división. Como EAXserá positivo (es decir, sin signo), esto tiene el efecto de poner a cero EDX, como antes.
- Luego está la división, esta vez dividida
EDX:EAXpor el valor de entrada (una copia no molestada de la cual aún reside EDI). Esto es equivalente al módulo, con el resto adentro EDX. (El cociente también se incluye EAX, pero no lo necesitamos).
- Finalmente,
XCHG(intercambiamos) los contenidos de EAXy EDX. Normalmente, harías un MOVaquí, pero XCHGes solo 1 byte (aunque más lento). Debido a que EDXcontiene el resto después de la división, será 0 si el valor era igualmente divisible o distinto de cero. Por lo tanto, cuando RETurnamos, EAX(el resultado) es 0 si el valor de entrada era divisible por el doble de la suma de sus dígitos, o de lo contrario no sería cero.
Esperemos que eso sea suficiente para una explicación.
Esta no es la entrada más corta, pero bueno, ¡parece que supera a casi todos los idiomas que no son de golf! :-)