x86-64マシンコード関数、40バイト。
または、strcmpのように「真」として0対非ゼロが許可されている場合は37バイト。
ビットマップのアイデアに対するKarl NapfのCの回答のおかげで、x86はBTSで非常に効率的に実行できます。
関数シグネチャ:_Bool cube_digits_same(uint64_t n);
、x86-64 System V ABIを使用。(n
RDIでは、ALのブール戻り値(0または1))。
_Bool
ISO C11で定義されており、通常はC ++と同じセマンティクスで#include <stdbool.h>
定義するために使用されます。bool
bool
潜在的な節約:
- 3バイト:逆条件を返します(違いがある場合はゼロ以外)。またはインラインasmから:フラグ条件を返します(gcc6で可能)
- 1バイト:EBXを上書きできる場合(そうすると、この関数に非標準の呼び出し規則が与えられます)。(インラインasmから実行できます)
- 1バイト:RET命令(インラインasmから)
これが関数ではなくインラインasmフラグメントである場合、これらはすべて可能です。これにより、inline-asmで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.
ループは、1回繰り返す最小の方法のようです。また、ループの繰り返し(REXプレフィックスなし、異なるビットマップレジスタなし)を検討しましたが、それはわずかに大きくなります。また、PUSH RSIを使用し、test spl, 0xf
/ jz
を使用して1回ループしました(ABIではRSPがCALLの前に16Bアラインされる必要があるため、1回押すとアラインし、もう1つはミスアラインします)。test r32, imm8
エンコードがないため、最小の方法は4B TEST命令(REXプレフィックスを含む)を使用して、imm8に対してRSPの下位バイトのみをテストすることでした。LEA + LOOPと同じサイズですが、追加のPUSH / POP命令が必要です。
テスト範囲内のすべてのnに対してテスト済み、対ステディボックスのC実装(異なるアルゴリズムを使用しているため)。私が見た結果が異なる2つのケースでは、コードは正しく、steadyboxのコードは間違っていました。私のコードはすべての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実装のバージョンに対してテストされ、結果はすべての入力に一致します。