x86-64 Machine Code, 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
The above code defines a function in 64-bit x86 machine code that determines whether the input value is divisible by double the sum of its digits. The function conforms to the System V AMD64 calling convention, so that it is callable from virtually any language, just as if it were a C function.
It takes a single parameter as input via the EDI
register, as per the calling convention, which is the integer to test. (This is assumed to be a positive integer, consistent with the challenge rules, and is required for the CDQ
instruction we use to work correctly.)
It returns its result in the EAX
register, again, as per the calling convention. The result will be 0 if the input value was divisible by the sum of its digits, and non-zero otherwise. (Basically, an inverse Boolean, exactly like the example given in the challenge rules.)
Its C prototype would be:
int DivisibleByDoubleSumOfDigits(int value);
Here are the ungolfed assembly language instructions, annotated with a brief explanation of the purpose of each 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
In the first block, we do some preliminary initialization of registers:
PUSH
+POP
instructions are used as a slow but short way to initialize ESI
to 10. This is necessary because the DIV
instruction on x86 requires a register operand. (There is no form that divides by an immediate value of, say, 10.)
XOR
is used as a short and fast way to clear the ECX
register. This register will serve as the "accumulator" inside of the upcoming loop.
- Finally, a copy of the input value (from
EDI
) is made, and stored in EAX
, which will be clobbered as we go through the loop.
Then, we start looping and summing the digits in the input value. This is based on the x86 DIV
instruction, which divides EDX:EAX
by its operand, and returns the quotient in EAX
and the remainder in EDX
. What we'll do here is divide the input value by 10, such that the remainder is the digit in the last place (which we'll add to our accumulator register, ECX
), and the quotient is the remaining digits.
- The
CDQ
instruction is a short way of setting EDX
to 0. It actually sign-extends the value in EAX
to EDX:EAX
, which is what DIV
uses as the dividend. We don't actually need sign-extension here, because the input value is unsigned, but CDQ
is 1 byte, as opposed to using XOR
to clear EDX
, which would be 2 bytes.
- Then we
DIV
ide EDX:EAX
by ESI
(10).
- The remainder (
EDX
) is added to the accumulator (ECX
).
- The
EAX
register (the quotient) is tested to see if it is equal to 0. If so, we have made it through all of the digits and we fall through. If not, we still have more digits to sum, so we go back to the top of the loop.
Finally, after the loop is finished, we implement number % ((sum_of_digits)*2)
:
The LEA
instruction is used as a short way to multiply ECX
by 2 (or, equivalently, add ECX
to itself), and store the result in a different register (in this case, EAX
).
(We could also have done add ecx, ecx
+xchg ecx, eax
; both are 3 bytes, but the LEA
instruction is faster and more typical.)
Then, we do a CDQ
again to prepare for division. Because EAX
will be positive (i.e., unsigned), this has the effect of zeroing EDX
, just as before.
Next is the division, this time dividing EDX:EAX
by the input value (an unmolested copy of which still resides in EDI
). This is equivalent to modulo, with the remainder in EDX
. (The quotient is also put in EAX
, but we don't need it.)
Finally, we XCHG
(exchange) the contents of EAX
and EDX
. Normally, you would do a MOV
here, but XCHG
is only 1 byte (albeit slower). Because EDX
contains the remainder after the division, it will be 0 if the value was evenly divisible or non-zero otherwise. Thus, when we RET
urn, EAX
(the result) is 0 if the input value was divisible by double the sum of its digits, or non-zero otherwise.
Hopefully that suffices for an explanation.
This isn't the shortest entry, but hey, it looks like it beats almost all of the non-golfing languages! :-)