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 EDIregister, sesuai dengan konvensi pemanggilan, yang merupakan bilangan bulat untuk diuji. (Ini dianggap bilangan bulat positif , konsisten dengan aturan tantangan, dan diperlukan untuk CDQinstruksi yang kami gunakan untuk bekerja dengan benar.)
Ini mengembalikan hasilnya dalam EAXregister, 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+ POPinstruksi digunakan sebagai cara lambat tapi pendek untuk menginisialisasi ESIke 10. Ini diperlukan karena DIVinstruksi pada x86 memerlukan operan register. (Tidak ada bentuk yang membelah dengan nilai langsung, katakanlah, 10.)
XORdigunakan sebagai cara singkat dan cepat untuk menghapus ECXregister. 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 DIVinstruksi x86 , yang membaginya EDX:EAXdengan operandnya, dan mengembalikan hasil bagi EAXdan 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
CDQinstruksi adalah cara singkat pengaturan EDXke 0. Ini sebenarnya tanda-meluas nilai dalam EAXuntuk EDX:EAX, yang adalah apa yang DIVmenggunakan sebagai dividen. Kami sebenarnya tidak perlu ekstensi-tanda di sini, karena nilai input tidak ditandatangani, tetapi CDQ1 byte, sebagai lawan menggunakan XORuntuk menghapus EDX, yang akan menjadi 2 byte.
- Lalu kami
DIVide EDX:EAXdengan ESI(10).
- Sisa (
EDX) ditambahkan ke akumulator ( ECX).
- The
EAXregister (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 LEAinstruksi digunakan sebagai cara singkat untuk kalikan ECXdengan 2 (atau, sama, menambahkan ECXuntuk 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 LEAinstruksi lebih cepat dan lebih khas.)
- Kemudian, kami melakukan
CDQlagi untuk mempersiapkan pembagian. Karena EAXakan positif (yaitu, tidak ditandatangani), ini memiliki efek zeroing EDX, seperti sebelumnya.
- Berikutnya adalah divisi, kali ini membaginya
EDX:EAXdengan 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 EAXdan EDX. Biasanya, Anda akan melakukannya di MOVsini, tetapi XCHGhanya 1 byte (walaupun lebih lambat). Karena EDXberisi sisa setelah pembagian, itu akan menjadi 0 jika nilainya dibagi rata atau tidak nol. Jadi, ketika kita RETurn, 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! :-)