Función de código de máquina x86-64, 40 bytes.
O 37 bytes si se permite 0 frente a cero como "verdadero", como strcmp.
Gracias a la respuesta C de Karl Napf para la idea de mapa de bits, que x86 puede hacer de manera muy eficiente con BTS .
Firma de la función:, _Bool cube_digits_same(uint64_t n);
utilizando el x86-64 System V ABI. ( n
en RDI, valor de retorno booleano (0 o 1) en AL).
_Bool
está definido por ISO C11, y generalmente se usa #include <stdbool.h>
para definir bool
con la misma semántica que C ++ bool
.
Posibles ahorros:
- 3 bytes: devuelve la condición inversa (distinta de cero si hay una diferencia). O desde asm en línea: devolver una condición de marca (que es posible con gcc6)
- 1 byte: si pudiéramos clobber EBX (hacerlo le daría a esta función una convención de llamada no estándar). (podría hacer eso desde inm asm)
- 1 byte: la instrucción RET (desde inm asm)
Todo esto es posible si se tratara de un fragmento inline-asm en lugar de una función, lo que lo convertiría en 35 bytes para 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 parece ser la forma más pequeña de repetir una vez. También miré solo repetir el ciclo (sin prefijos REX y un registro de mapa de bits diferente), pero eso es un poco más grande. También intenté usar PUSH RSI y usar test spl, 0xf
/ jz
to loop una vez (ya que el ABI requiere que RSP esté alineado 16B antes de CALL, por lo que un impulso lo alinea y otro lo desalinea nuevamente). No hay test r32, imm8
codificación, por lo que la forma más pequeña fue con una instrucción 4B TEST (incluido un prefijo REX) para probar solo el byte bajo de RSP contra un imm8. Mismo tamaño que LEA + LOOP, pero con instrucciones adicionales PUSH / POP requeridas.
Probado para todos los n en el rango de prueba, frente a la implementación C de steadybox (ya que utiliza un algoritmo diferente). En los dos casos de resultados diferentes que miré, mi código era correcto y el de steadybox estaba equivocado. Creo que mi código es correcto para todos los 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);
}
}
Las únicas líneas impresas tienen c = 1 asm = 0: falsos positivos para el algoritmo C.
También se probó con una uint64_t
versión de la implementación C de Karl del mismo algoritmo, y los resultados coinciden con todas las entradas.