Código de máquina x86-64, 44 bytes
(El mismo código de máquina también funciona en modo de 32 bits).
La respuesta de @Daniel Schepler fue un punto de partida para esto, pero tiene al menos una nueva idea algorítmica (no solo un mejor golf de la misma idea): los códigos ASCII para 'B'
( 1000010
) y 'X'
( 1011000
) dan 16 y 2 después de enmascarar con0b0010010
.
Entonces, después de excluir decimal (dígito inicial distinto de cero) y octal (char después '0'
es menor que 'B'
), podemos establecer base = c & 0b0010010
y saltar al ciclo de dígitos.
Se puede llamar con x86-64 System V como unsigned __int128 parse_cxx14_int(int dummy, const char*rsi);
Extrae el valor de retorno EDX de la mitad alta del unsigned __int128
resultado con tmp>>64
.
.globl parse_cxx14_int
## Input: pointer to 0-terminated string in RSI
## output: integer in EDX
## clobbers: RAX, RCX (base), RSI (points to terminator on return)
parse_cxx14_int:
xor %eax,%eax # initialize high bits of digit reader
cdq # also initialize result accumulator edx to 0
lea 10(%rax), %ecx # base 10 default
lodsb # fetch first character
cmp $'0', %al
jne .Lentry2
# leading zero. Legal 2nd characters are b/B (base 2), x/X (base 16)
# Or NUL terminator = 0 in base 10
# or any digit or ' separator (octal). These have ASCII codes below the alphabetic ranges
lodsb
mov $8, %cl # after '0' have either digit, apostrophe, or terminator,
cmp $'B', %al # or 'b'/'B' or 'x'/'X' (set a new base)
jb .Lentry2 # enter the parse loop with base=8 and an already-loaded character
# else hex or binary. The bit patterns for those letters are very convenient
and $0b0010010, %al # b/B -> 2, x/X -> 16
xchg %eax, %ecx
jmp .Lentry
.Lprocessdigit:
sub $'0' & (~32), %al
jb .Lentry # chars below '0' are treated as a separator, including '
cmp $10, %al
jb .Lnum
add $('0'&~32) - 'A' + 10, %al # digit value = c-'A' + 10. we have al = c - '0'&~32.
# c = al + '0'&~32. val = m+'0'&~32 - 'A' + 10
.Lnum:
imul %ecx, %edx
add %eax, %edx # accum = accum * base + newdigit
.Lentry:
lodsb # fetch next character
.Lentry2:
and $~32, %al # uppercase letters (and as side effect,
# digits are translated to N+16)
jnz .Lprocessdigit # space also counts as a terminator
.Lend:
ret
Los bloques modificados contra la versión de Daniel tienen (en su mayoría) sangría menos que otras instrucciones. Además, el bucle principal tiene su rama condicional en la parte inferior. Esto resultó ser un cambio neutral porque ninguno de los dos caminos podía caer en la parte superior y eldec ecx / loop .Lentry
idea de ingresar al bucle resultó no ser una victoria después de manejar el octal de manera diferente. Pero tiene menos instrucciones dentro del bucle con el bucle en forma idiomática do {} mientras estructura, así que lo guardé.
El arnés de prueba C ++ de Daniel funciona sin cambios en el modo de 64 bits con este código, que utiliza la misma convención de llamada que su respuesta de 32 bits.
g++ -Og parse-cxx14.cpp parse-cxx14.s &&
./a.out < tests | diff -u -w - tests.good
Desmontaje, incluidos los bytes del código de máquina que son la respuesta real
0000000000000000 <parse_cxx14_int>:
0: 31 c0 xor %eax,%eax
2: 99 cltd
3: 8d 48 0a lea 0xa(%rax),%ecx
6: ac lods %ds:(%rsi),%al
7: 3c 30 cmp $0x30,%al
9: 75 1c jne 27 <parse_cxx14_int+0x27>
b: ac lods %ds:(%rsi),%al
c: b1 08 mov $0x8,%cl
e: 3c 42 cmp $0x42,%al
10: 72 15 jb 27 <parse_cxx14_int+0x27>
12: 24 12 and $0x12,%al
14: 91 xchg %eax,%ecx
15: eb 0f jmp 26 <parse_cxx14_int+0x26>
17: 2c 10 sub $0x10,%al
19: 72 0b jb 26 <parse_cxx14_int+0x26>
1b: 3c 0a cmp $0xa,%al
1d: 72 02 jb 21 <parse_cxx14_int+0x21>
1f: 04 d9 add $0xd9,%al
21: 0f af d1 imul %ecx,%edx
24: 01 c2 add %eax,%edx
26: ac lods %ds:(%rsi),%al
27: 24 df and $0xdf,%al
29: 75 ec jne 17 <parse_cxx14_int+0x17>
2b: c3 retq
Otros cambios de la versión de Daniel incluyen guardar el sub $16, %al
desde el interior del bucle de dígitos, usando más en sub
lugar detest
como parte de la detección de separadores, y dígitos versus caracteres alfabéticos.
A diferencia de Daniel, cada personaje a continuación '0'
se trata como un separador, no solo '\''
. (Excepto ' '
:and $~32, %al
/ jnz
en nuestros dos bucles trata el espacio como un terminador, lo que posiblemente sea conveniente para probar con un número entero al comienzo de una línea).
Cada operación que se modifica %al
dentro del bucle tiene una rama que consume banderas establecidas por el resultado, y cada rama va (o cae) a una ubicación diferente.