x86-64 मशीन कोड फ़ंक्शन, 40 बाइट्स।
या 37 बाइट्स यदि 0 बनाम गैर-शून्य को "सत्य" के रूप में अनुमति दी जाती है, तो स्ट्रैम्प की तरह।
बिटमैप विचार के लिए कार्ल नेपफ के सी उत्तर के लिए धन्यवाद, जो बी 86 बीटीएस के साथ बहुत कुशलता से कर सकता है ।
फंक्शन सिग्नेचर: _Bool cube_digits_same(uint64_t n);
x86-64 सिस्टम V ABI का उपयोग करके। ( n
RDI में, बूलियन रिटर्न वैल्यू (0 या 1) AL में)।
_Bool
ISO C11 द्वारा परिभाषित किया गया है, और आमतौर पर C ++ के समान शब्दार्थ के साथ #include <stdbool.h>
परिभाषित करने के लिए उपयोग किया जाता है ।bool
bool
संभावित बचत:
- 3 बाइट्स: उलटा स्थिति (गैर-शून्य अगर कोई अंतर है) लौटना। या इनलाइन एएसएम से: एक फ्लैग कंडीशन लौटाना (जो कि gcc6 के साथ संभव है)
- 1 बाइट: अगर हम ईबीएक्स को बंद कर सकते हैं (ऐसा करने से यह फ़ंक्शन एक गैर-मानक कॉलिंग सम्मेलन होगा)। (इनलाइन asm से ऐसा कर सकते हैं)
- 1 बाइट: आरईटी निर्देश (इनलाइन एएसएम से)
ये सभी संभव हैं यदि यह एक फ़ंक्शन के बजाय एक इनलाइन-एएसएम टुकड़ा था, जो इनलाइन-एएसएम के लिए 35 बाइट्स बना देगा ।
0000000000000000 <cube_digits_same>:
0: 89 f8 mov eax,edi
2: 48 f7 e7 mul rdi # can't avoid a REX prefix: 2642245^2 doesn't fit in 32 bits
5: 48 f7 e7 mul rdi # rax = n^3, rdx=0
8: 44 8d 52 0a lea r10d,[rdx+0xa] # EBX would save a REX prefix, but it's call-preserved in this ABI.
c: 8d 4a 02 lea ecx,[rdx+0x2]
000000000000000f <cube_digits_same.repeat>:
f: 31 f6 xor esi,esi
0000000000000011 <cube_digits_same.cube_digits>:
11: 31 d2 xor edx,edx
13: 49 f7 f2 div r10 ; rax = quotient. rdx=LSB digit
16: 0f ab d6 bts esi,edx ; esi |= 1<<edx
19: 48 85 c0 test rax,rax ; Can't skip the REX: (2^16 * 10)^3 / 10 has all-zero in the low 32.
1c: 75 f3 jne 11 <cube_digits_same.cube_digits>
; 1st iter: 2nd iter: both:
1e: 96 xchg esi,eax ; eax=n^3 bitmap eax=n bitmap esi=0
1f: 97 xchg edi,eax ; edi=n^3 bitmap, eax=n edi=n bmp, eax=n^3 bmp
20: e2 ed loop f <cube_digits_same.repeat>
22: 39 f8 cmp eax,edi
24: 0f 94 d0 sete al
;; The ABI says it's legal to leave garbage in the high bytes of RAX for narrow return values
;; so leaving the high 2 bits of the bitmap in AH is fine.
27: c3 ret
0x28: end of function.
LOOP एक बार दोहराने का सबसे छोटा तरीका लगता है। मैंने केवल लूप को दोहराते हुए देखा (REX उपसर्गों के बिना, और एक अलग बिटमैप रजिस्टर), लेकिन यह थोड़ा बड़ा है। मैं भी PUSH RSI उपयोग कर, और उपयोग करने की कोशिश test spl, 0xf
/ jz
पाश करने के लिए एक बार (के बाद से ABI की आवश्यकता है कि आरएसपी 16B कॉल करने से पहले गठबंधन है, इसलिए एक धक्का यह संरेखित करता है, और एक अन्य इसे फिर से misaligns)। कोई test r32, imm8
एन्कोडिंग नहीं है , इसलिए सबसे छोटा तरीका एक 4 बी टेस्ट इंस्ट्रक्शन (एक आरईएक्स प्रीफिक्स सहित) के साथ आरएसपी के कम बाइट को एक इम 8 के खिलाफ परीक्षण करने के लिए था। LEA + LOOP के समान आकार, लेकिन आवश्यक अतिरिक्त PUSH / POP निर्देशों के साथ।
परीक्षण रेंज में सभी एन के लिए परीक्षण किया गया, बनाम स्टेबबॉक्स का सी कार्यान्वयन (क्योंकि यह एक अलग एल्गोरिथ्म का उपयोग करता है)। अलग-अलग परिणामों के दो मामलों में जो मैंने देखा, मेरा कोड सही था और स्टैब्लबॉक्स गलत था। मुझे लगता है कि मेरा कोड सभी n के लिए सही है।
_Bool cube_digits_same(unsigned long long n);
#include <stdio.h>
#include <stdbool.h>
int main()
{
for(unsigned n=0 ; n<= 2642245 ; n++) {
bool c = f(n);
bool asm_result = cube_digits_same(n);
if (c!=asm_result)
printf("%u problem: c=%d asm=%d\n", n, (int)c, (int)asm_result);
}
}
मुद्रित की गई एकमात्र पंक्तियों में c = 1 asm = 0 है: C एल्गोरिथ्म के लिए गलत-सकारात्मक।
एक uint64_t
ही एल्गोरिथ्म के कार्ल के कार्यान्वयन के एक संस्करण के खिलाफ भी परीक्षण किया गया , और सभी इनपुट के लिए परिणाम मेल खाते हैं।