Функція машинного коду x86-64, 40 байт.
Або 37 байт, якщо 0 проти не нуля дозволено як "truthy", як strcmp.
Завдяки C відповіді Карла Напфа за раціональну ідею, яку x86 може зробити дуже ефективно з BTS .
Підпис функції: _Bool cube_digits_same(uint64_t n);
за допомогою системи x86-64 System V ABI. ( n
в RDI, булеве значення повернення (0 або 1) в AL).
_Bool
визначається ISO C11, і зазвичай використовується #include <stdbool.h>
для визначення bool
з тією ж семантикою, що і C ++ bool
.
Потенційна економія:
- 3 байти: Повернення оберненої умови (ненульової, якщо є різниця). Або з вбудованого asm: повернення умови прапора (що можливо з gcc6)
- 1 байт: Якби ми могли сковувати EBX (це зробило б цю функцію нестандартною умовою виклику). (міг би зробити це з inline asm)
- 1 байт: інструкція RET (від вбудованого ASM)
Все це можливо, якби це був вбудований фрагмент asm замість функції, яка зробила б його 35 байтами для inline-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.
LOOP здається найменшим способом повторити один раз. Я також дивився на просто повторення циклу (без префіксів REX та іншого реєстру растрових зображень), але це трохи більше. Я також спробував використовувати PUSH RSI і один раз використовувати цикл test spl, 0xf
/ jz
для циклу (оскільки ABI вимагає, щоб RSP вирівнювався 16B перед CALL, тому один натискання вирівнює його, а інший знову вирівнює). test r32, imm8
Кодування немає , тому найменший спосіб полягав у інструкції 4B TEST (включаючи префікс REX), щоб перевірити лише низький байт RSP проти імунітету8. Той самий розмір, що і LEA + LOOP, але потрібні додаткові інструкції PUSH / POP.
Тестується для всіх n в тестовому діапазоні, порівняно з реалізацією C на постійній панелі (оскільки він використовує інший алгоритм). У двох випадках із різними результатами, які я розглянув, мій код був правильним, а стійка скринька помилялася. Я думаю, що мій код правильний для всіх 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
версія версії C Карла за тим же алгоритмом, і результати відповідають для всіх даних.