x86-64 머신 코드, 44 바이트
동일한 머신 코드는 32 비트 모드에서도 작동합니다.
@Daniel Schepler의 대답 은 이것의 출발점이지만, 적어도 하나의 새로운 알고리즘 아이디어가 있습니다 (동일한 아이디어의 더 나은 골프뿐만 아니라) : ( ) 및 ( )에 대한 ASCII 코드는'B'
1000010
'X'
1011000
0b0010010
.
따라서 10 진수 (0이 아닌 선행 자릿수)와 8 진수 (char after '0'
is less than 'B'
)를 제외한 후 base =c & 0b0010010
하고 숫자 루프로 건너 뛸 .
x86-64 시스템 V를 사용하여 호출 가능 결과 unsigned __int128 parse_cxx14_int(int dummy, const char*rsi);
의 상위 절반에서 EDX 반환 값을 추출합니다 .unsigned __int128
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
변경된 블록 대 Daniel의 버전은 (대부분) 다른 명령보다 들여 쓰기가 적습니다. 또한 메인 루프는 하단에 조건부 분기가 있습니다. 어느 경로도 그것의 꼭대기에 빠질 수 없기 때문에 이것은 중립적 인 변화로 판명되었습니다.dec ecx / loop .Lentry
판명되었으며 루프에 들어가는 아이디어는 8 진수를 다르게 처리 한 후에 승리하지 않는 것으로 나타났습니다. 그러나 구조 중에는 관용적 형태의 루프가 {{} 인 루프를 사용하는 루프 내부에 명령이 적으므로 계속 유지했습니다.
Daniel의 C ++ 테스트 하니스는 64 비트 모드에서 32 비트 응답과 동일한 호출 규칙을 사용하는이 코드를 사용하여 변경없이 작동합니다.
g++ -Og parse-cxx14.cpp parse-cxx14.s &&
./a.out < tests | diff -u -w - tests.good
실제 답변 인 머신 코드 바이트를 포함하여 분해
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
Daniel 버전의 다른 변경 사항은 대신에 sub $16, %al
더 많이 사용하여 숫자 루프 내부에서 저장하는 것을 포함합니다.sub
test
구분 기호를 감지하는 문자 것과 알파벳 대 문자를 포함합니다.
다니엘과 달리 아래 의 모든 문자 '0'
는 단지 구분 기호로 취급되지 않습니다 '\''
. (제외 ' '
: and $~32, %al
/ jnz
. 라인의 시작 정수와 테스트 가능성이 편리한 종결 등 모두 우리의 루프 취급 공간에서)
%al
루프 내부 를 수정하는 모든 작업 에는 결과에 의해 설정된 분기 소비 플래그가 있으며 각 분기는 다른 위치로 이동합니다.