x86-64 Машинний код, 24 байти
6A 0A 5E 31 C9 89 F8 99 F7 F6 01 D1 85 C0 75 F7 8D 04 09 99 F7 F7 92 C3
Вищевказаний код визначає функцію в 64-розрядному машинному коді x86, яка визначає, чи вхідне значення ділиться на подвійну суму його цифр. Функція відповідає умові виклику System V AMD64, так що її можна дзвонити практично з будь-якої мови, як би це була функція C.
Він приймає один параметр як вхід через EDIрегістр, згідно з умовою виклику, що є цілим числом для тестування. (Передбачається, що це натуральне число, відповідне правилам виклику, і воно потрібне для CDQінструкції, яку ми використовуємо для правильної роботи.)
EAXЗнову він повертає свій результат у регістр, відповідно до конвенції, що викликає. Результат буде 0, якщо вхідне значення було розділене на суму його цифр, а не нульове в іншому випадку. (В основному, зворотний булевий, точно як приклад, наведений у правилах виклику.)
Його прототипом C було б:
int DivisibleByDoubleSumOfDigits(int value);
Ось невідомі інструкції з мови збірки, анотація з коротким поясненням мети кожної інструкції:
; 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
У першому блоці робимо попередню ініціалізацію регістрів:
PUSH+ POPінструкції використовуються як повільний, але короткий спосіб ініціалізації ESIдо 10. Це необхідно, оскільки DIVінструкція на x86 вимагає операнду реєстру. (Немає форми, яка ділиться на безпосереднє значення, скажімо, 10.)
XORвикористовується як короткий і швидкий спосіб очищення ECXреєстру. Цей регістр буде служити "акумулятором" всередині майбутнього циклу.
- Нарешті, робиться копія вхідного значення (від
EDI) і зберігається в ньому EAX, який буде клобуватися, коли ми проходимо цикл.
Потім ми починаємо циклічне підсумовування та підсумовування цифр у вхідному значенні. Це засновано на DIVінструкції x86 , яка ділиться EDX:EAXза операндом і повертає коефіцієнт на, EAXа решту в EDX. Що ми тут зробимо, це розділити вхідне значення на 10, таким чином, що залишок - це цифра на останньому місці (яку ми додамо до нашого реєстру акумуляторів ECX), а коефіцієнт - цифри, що залишилися.
CDQІнструкція короткий шлях установки EDX0. Це фактично знаково-розширює значення в EAXдо EDX:EAX, що і DIVвикористовує в якості дивідендів. Тут насправді не потрібно розширення знаків, оскільки вхідне значення не підписане, але CDQце 1 байт, на відміну від використання XORдля очищення EDX, яке було б 2 байти.
- Тоді ми
DIVіде EDX:EAXпо ESI(10).
- Залишок (
EDX) додається до акумулятора ( ECX).
EAXРегістр (фактор) перевіряється , щоб побачити , якщо він дорівнює 0. Якщо це так, ми зробили це через всі цифри , і ми провалитися. Якщо ні, у нас ще є цифри для підсумовування, тому ми повертаємося до вершини циклу.
Нарешті, після закінчення циклу ми реалізуємо number % ((sum_of_digits)*2):
LEAІнструкція використовується як короткий спосіб помножити ECXна 2 (або, що те ж саме, додати ECXдо себе), і зберегти результат в іншому регістрі (в даному випадку, EAX).
(Ми також могли зробити add ecx, ecx+ xchg ecx, eax; обидва - 3 байти, але LEAінструкція швидша і типовіша.)
- Потім робимо ще
CDQраз, щоб підготуватися до поділу. Оскільки EAXбуде позитивним (тобто непідписаним), це матиме ефект нулю EDX, як і раніше.
- Далі йде поділ, цей час ділиться
EDX:EAXна вхідне значення (непомітна копія якого все ще знаходиться в EDI). Це еквівалентно модулю, а решта в EDX. (Коефіцієнт також вводиться EAX, але він нам не потрібен.)
- Нарешті, ми
XCHG(обмінюємось) вмістом EAXта EDX. Зазвичай ви робите MOVтут, але XCHGце лише 1 байт (хоч і повільніше). Оскільки EDXміститься залишок після поділу, він буде дорівнює 0, якщо значення було рівномірно розділене або ненульове в іншому випадку. Таким чином, коли ми робимо RETурну, EAX(результат) дорівнює 0, якщо вхідне значення було розділене на подвійну суму його цифр, або не нульове в іншому випадку.
Сподіваємось, цього достатньо для пояснення.
Це не найкоротший запис, але ей, схоже, він перемагає майже всі мови, що не гольфують! :-)