x86-64 Kode Mesin, 24 byte
6A 0A 5E 31 C9 89 F8 99 F7 F6 01 D1 85 C0 75 F7 8D 04 09 99 F7 F7 92 C3
Kode di atas mendefinisikan fungsi dalam kode mesin x86 64-bit yang menentukan apakah nilai input dapat dibagi dengan menggandakan jumlah digitnya. Fungsi ini sesuai dengan konvensi pemanggilan System V AMD64, sehingga dapat dipanggil dari hampir semua bahasa, sama seperti fungsi C.
Dibutuhkan parameter tunggal sebagai input melalui EDI
register, sesuai dengan konvensi pemanggilan, yang merupakan bilangan bulat untuk diuji. (Ini dianggap bilangan bulat positif , konsisten dengan aturan tantangan, dan diperlukan untuk CDQ
instruksi yang kami gunakan untuk bekerja dengan benar.)
Ini mengembalikan hasilnya dalam EAX
register, sekali lagi, sesuai dengan konvensi pemanggilan. Hasilnya akan 0 jika nilai masukan adalah dibagi dengan jumlah digit, dan non-nol sebaliknya. (Pada dasarnya, Boolean terbalik, persis seperti contoh yang diberikan dalam aturan tantangan.)
Prototipe C-nya adalah:
int DivisibleByDoubleSumOfDigits(int value);
Berikut adalah instruksi bahasa majelis yang tidak diseragamkan, dijelaskan dengan penjelasan singkat tentang tujuan dari setiap instruksi:
; 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
Di blok pertama, kami melakukan beberapa inisialisasi awal register:
PUSH
+ POP
instruksi digunakan sebagai cara lambat tapi pendek untuk menginisialisasi ESI
ke 10. Ini diperlukan karena DIV
instruksi pada x86 memerlukan operan register. (Tidak ada bentuk yang membelah dengan nilai langsung, katakanlah, 10.)
XOR
digunakan sebagai cara singkat dan cepat untuk menghapus ECX
register. Register ini akan berfungsi sebagai "akumulator" di dalam loop yang akan datang.
- Akhirnya, salinan nilai input (dari
EDI
) dibuat, dan disimpan EAX
, yang akan musnah saat kita melewati loop.
Kemudian, kita mulai mengulang dan menjumlahkan digit dalam nilai input. Ini didasarkan pada DIV
instruksi x86 , yang membaginya EDX:EAX
dengan operandnya, dan mengembalikan hasil bagi EAX
dan sisanya masuk EDX
. Apa yang akan kita lakukan di sini adalah membagi nilai input dengan 10, sehingga sisanya adalah digit di tempat terakhir (yang akan kita tambahkan ke register akumulator kami, ECX
), dan hasil bagi adalah digit yang tersisa.
- The
CDQ
instruksi adalah cara singkat pengaturan EDX
ke 0. Ini sebenarnya tanda-meluas nilai dalam EAX
untuk EDX:EAX
, yang adalah apa yang DIV
menggunakan sebagai dividen. Kami sebenarnya tidak perlu ekstensi-tanda di sini, karena nilai input tidak ditandatangani, tetapi CDQ
1 byte, sebagai lawan menggunakan XOR
untuk menghapus EDX
, yang akan menjadi 2 byte.
- Lalu kami
DIV
ide EDX:EAX
dengan ESI
(10).
- Sisa (
EDX
) ditambahkan ke akumulator ( ECX
).
- The
EAX
register (hasil bagi) diuji untuk melihat apakah itu adalah sama dengan 0. Jika demikian, kita telah berhasil melewati semua angka dan kami jatuh. Jika tidak, kami masih memiliki lebih banyak digit untuk dijumlahkan, jadi kami kembali ke atas loop.
Akhirnya, setelah loop selesai, kami menerapkan number % ((sum_of_digits)*2)
:
The LEA
instruksi digunakan sebagai cara singkat untuk kalikan ECX
dengan 2 (atau, sama, menambahkan ECX
untuk dirinya sendiri), dan menyimpan hasilnya dalam register yang berbeda (dalam hal ini, EAX
).
(Kami juga bisa melakukan add ecx, ecx
+ xchg ecx, eax
; keduanya 3 byte, tetapi LEA
instruksi lebih cepat dan lebih khas.)
- Kemudian, kami melakukan
CDQ
lagi untuk mempersiapkan pembagian. Karena EAX
akan positif (yaitu, tidak ditandatangani), ini memiliki efek zeroing EDX
, seperti sebelumnya.
- Berikutnya adalah divisi, kali ini membaginya
EDX:EAX
dengan nilai input (salinan tanpa gangguan yang masih berada di dalamnya EDI
). Ini setara dengan modulo, dengan sisanya dalam EDX
. (Hasil bagi juga dimasukkan EAX
, tetapi kami tidak membutuhkannya.)
- Akhirnya, kami
XCHG
(bertukar) isi EAX
dan EDX
. Biasanya, Anda akan melakukannya di MOV
sini, tetapi XCHG
hanya 1 byte (walaupun lebih lambat). Karena EDX
berisi sisa setelah pembagian, itu akan menjadi 0 jika nilainya dibagi rata atau tidak nol. Jadi, ketika kita RET
urn, EAX
(hasilnya) adalah 0 jika nilai input dibagi dengan dua kali lipat jumlah digitnya, atau bukan nol.
Semoga cukup untuk penjelasan.
Ini bukan entri terpendek, tapi hei, sepertinya itu mengalahkan hampir semua bahasa non-golf! :-)