x86-64 makine kodu işlevi, 40 bayt.
Veya 0'a karşılık sıfır olmayana strcmp gibi "truthy" olarak izin verilirse 37 bayt.
Karl Napf'ın C'si sayesinde, x86'nın BTS ile çok verimli bir şekilde yapabileceği bitmap fikrine cevap .
İşlev imzası:, _Bool cube_digits_same(uint64_t n);
x86-64 System V ABI kullanılarak. ( n
RDI'da, boolean dönüş değeri (0 veya 1) AL cinsinden).
_Bool
ISO C11 ile tanımlanır ve tipik olarak C ++ ile aynı anlambilimiyle #include <stdbool.h>
tanımlamak için kullanılır .bool
bool
Potansiyel tasarruflar:
- 3 bayt: Ters koşulu döndürür (fark varsa sıfır olmayan). Veya satır içi asm'den: bayrak koşulu döndürmek (gcc6 ile mümkün)
- 1 byte: EBX'i engelleyebilseydik (böyle yaparsa bu fonksiyon standart olmayan bir çağrı kuralı olurdu). (satır içi asm bunu yapabilir)
- 1 bayt: RET komutu (satır içi asm'dan)
Bunların tümü, bir işlev yerine satır içi bir bölüm olsaydı , satır içi asm için 35 bayt yapabilecekse mümkün .
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 bir kez tekrar etmenin en küçük yolu gibi görünüyor. Ayrıca sadece döngüyü tekrarlamaya baktım (REX önekleri ve farklı bir bitmap kaydı olmadan), ama bu biraz daha büyük. Ayrıca PUSH RSI'yı kullanmayı ve test spl, 0xf
/ jz
döngüsünü bir kez kullanmayı da denedim (ABI, RSP'nin CALL'den önce 16B hizalı olmasını gerektirdiğinden, bir itme onu hizalar, diğeri yeniden hizalar). test r32, imm8
Kodlama yok , bu yüzden en küçük yol 4B TEST komutuyla (REX öneki dahil) bir imm8'e karşı RSP'nin sadece düşük baytını test etmekti. LEA + LOOP ile aynı boyutta, ancak ekstra PUSH / POP talimatları gerekli.
Steadybox'ın C uygulamasını (test farklı bir algoritma kullandığı için) vs. Baktığım farklı sonuçlardan iki tanesinde kodum doğruydu ve sabit kutu yanlıştı. Kodumun herkes için doğru olduğunu düşünüyorum.
_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);
}
}
Yazdırılan tek satırlar c = 1 asm = 0: C algoritması için yanlış pozitifler.
Ayrıca uint64_t
aynı algoritmanın Karl'ın C uygulamasının bir versiyonuna karşı test edildi ve sonuçlar tüm girdiler için uygun.