funkcja kodu maszynowego x86-64, 40 bajtów.
Lub 37 bajtów, jeśli 0 kontra niezerowe jest dozwolone jako „prawda”, np. Strcmp.
Dzięki odpowiedzi C Karla Napfa na pomysł na mapę bitową, który x86 może bardzo skutecznie zrobić z BTS .
Podpis funkcji: _Bool cube_digits_same(uint64_t n);
przy użyciu ABI x86-64 System V. ( n
w RDI: logiczna wartość zwracana (0 lub 1) w AL).
_Bool
jest zdefiniowany przez ISO C11 i jest zwykle używany #include <stdbool.h>
do definiowania bool
przy użyciu tej samej semantyki co C ++ bool
.
Potencjalne oszczędności:
- 3 bajty: Zwracanie warunku odwrotnego (niezerowego, jeśli występuje różnica). Lub z wbudowanego asm: zwracanie warunku flagi (co jest możliwe w gcc6)
- 1 bajt: Gdybyśmy mogli zablokować EBX (zrobienie tego nadałoby tej funkcji niestandardową konwencję wywoływania). (może to zrobić z wbudowanego asm)
- 1 bajt: instrukcja RET (z wbudowanego asm)
Wszystkie te są możliwe, jeśli byłby to fragment wbudowanego asm zamiast funkcji, co dałoby 35 bajtów dla wbudowanego asm .
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.
PĘTLA wydaje się najmniejszym sposobem powtórzenia raz. Patrzyłem również na powtórzenie pętli (bez prefiksów REX i innego rejestru bitmap), ale to nieco większe. Próbowałem także użyć PUSH RSI i użyć test spl, 0xf
/ jz
do zapętlenia raz (ponieważ ABI wymaga, aby RSP był wyrównany 16B przed CALL, więc jeden push wyrównuje go, a drugi ponownie wyrównuje). Nie ma test r32, imm8
kodowania, więc najmniejszym sposobem była instrukcja 4B TEST (w tym prefiks REX), aby przetestować tylko niski bajt RSP na imm8. Taki sam rozmiar jak LEA + PĘTLA, ale wymagane są dodatkowe instrukcje PUSH / POP.
Przetestowano dla wszystkich nw zakresie testowym vs. implementacja C steadyboksa (ponieważ używa innego algorytmu). W dwóch przypadkach różnych wyników, na które patrzyłem, mój kod był poprawny, a kod steadyboksa był niepoprawny. Myślę, że mój kod jest poprawny dla wszystkich 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);
}
}
Jedyne drukowane linie mają c = 1 asm = 0: fałszywie dodatnie dla algorytmu C.
Przetestowano również pod kątem uint64_t
implementacji tego samego algorytmu w wersji C Karla, a wyniki są zgodne dla wszystkich danych wejściowych.