如果我在 EDX-EAX 中有 64位数字,并且我将除以小 em> number,商可能会变成大于 32位的数字。
那么此时div
运算符只设置进位标志?
我的问题是,我想在 EDX-EAX 中处理一个数字并将其写出数字,所以在这种情况下我必须 10 10
答案 0 :(得分:3)
没有。 64b / 32b中的DIV
具有最大商2 32 -1。
溢出用#DE(除错误)异常表示,而不是CF标志。
如果64b号码有一些限制,不使用完整的64b(例如2 61 max),那么您可以先将其拆分为{{ 1}} 10 9 (最左边2 32 )首先然后做两个"一半"分别由div
。但正如杰斯特所指出的那样,64b div 10
是如此之慢,以10为幂的sub做声听起来更好,而且代码也会更简单。
因为如果简单,那么为什么不添加代码,对吧?将是〜5分钟......〜60分钟后(我对它不是很满意,我认为可以通过更短的代码以更优雅的方式完成...在任何一种情况下都不会打扰性能,这个一个可以肯定优化,最小化对齐重要的循环,但它至少有效,所以它可以是你的"参考版本"比较/验证... ...
NASM 32b linux可执行文件,保存到div
建立:uint64toascii.asm
nasm -f elf *.asm; ld -m elf_i386 -s -o uint64toascii *.o
您可以在http://www.tutorialspoint.com/compile_assembly_online.php网上试一试(在那里复制来源)
因为我不太喜欢第一个版本,所以我一直在玩它,主要是试图获得一个简短的(LoC)代码,而不是特别关心性能或代码大小(懒得测量)除了编辑器中的行数以外的任何东西。
构建命令行+现场演示,与前一种情况相同:
section .text
global _start ;must be declared for using gcc
_start: ;tell linker entry point
; allocate 24B temporary buffer for ASCII number
sub esp,24
; output test numbers in loop
mov esi,testnumbers
testNumbersLoop:
mov eax,[esi]
mov edx,[esi+4]
mov edi,esp
; call the routine
call integer64btoascii
; add new line to output
mov [edi],byte 10
inc edi
; display number string
mov edx,edi
sub edx,esp ; output length
mov ecx,esp ; output buffer address
mov ebx,1 ; file descriptor (stdout)
mov eax,4 ; system call number (sys_write)
int 0x80 ; call kernel
; loop through test numbers
add esi,8
cmp esi,testnumbersEND
jb testNumbersLoop
; exit
add esp,24 ; release temporary buffer
mov eax, 1 ; system call number (sys_exit)
int 0x80 ; call kernel
integer64btoascii:
; edx:eax = number to convert, edi = buffer to output (at least 20B)
; returns edi pointing after last character
push eax
push edx
push esi
push ebx
push ebp
push ecx
; test for zero in edx:eax -> special handling
mov esi,edx
or esi,eax
jz .zeroNumber
; convert other numbers by subtracting 10^k powers
mov esi,pow10table-8
.skipLeadingZero:
add esi,8
cmp edx,[esi+4]
jc .skipLeadingZero
jne .next10powerInit
cmp eax,[esi]
jc .skipLeadingZero
jmp .next10powerInit
; since here every power of 10 is counted and set into output
.next10power:
mov [edi],cl ; write counter digit of previous 10th power
inc edi
.next10powerInit:
mov ebx,[esi]
mov ebp,[esi+4] ; ebp:ebx = 10^k
test ebx,ebx
jz .finish ; only zero terminator can have lower 32b == 0
mov cl,'0'
add esi,8
.compare10power:
cmp edx,ebp
jc .next10power
jnz .sub10power
cmp eax,ebx
jc .next10power
.sub10power:
sub eax,ebx
sbb edx,ebp
inc cl
jmp .compare10power
.zeroNumber:
mov [edi],byte '0'
inc edi
.finish:
pop ecx
pop ebp
pop ebx
pop esi
pop edx
pop eax
ret
section .rodata
pow10table:
dq 10000000000000000000
dq 1000000000000000000
dq 100000000000000000
dq 10000000000000000
dq 1000000000000000
dq 100000000000000
dq 10000000000000
dq 1000000000000
dq 100000000000
dq 10000000000
dq 1000000000
dq 100000000
dq 10000000
dq 1000000
dq 100000
dq 10000
dq 1000
dq 100
dq 10
dq 1
dq 0 ; terminator
testnumbers:
dq ~0 ; max 2^64-1 = 18446744073709551615
dq 0 ; looks like zero to me
dd 0, 1 ; 2^32 = 4294967296
dq 1234567890 ; < 2^32 (edx = 0)
dq 9999999999999999999
dq 101001000100101
testnumbersEND:
顺便说一下,如果CPU不会因部分注册section .text
global _start ;must be declared for using gcc
_start: ;tell linker entry point
; allocate 24B temporary buffer for ASCII number
sub esp,24
; output test numbers in loop
mov esi,testnumbers
testNumbersLoop:
mov eax,[esi]
mov edx,[esi+4]
mov edi,esp
; call the routine
call integer64btoascii
; add new line to output
mov [edi],byte 10
inc edi
; display number string
mov edx,edi
sub edx,esp ; output length
mov ecx,esp ; output buffer address
mov ebx,1 ; file descriptor (stdout)
mov eax,4 ; system call number (sys_write)
int 0x80 ; call kernel
; loop through test numbers
add esi,8
cmp esi,testnumbersEND
jb testNumbersLoop
; exit
add esp,24 ; release temporary buffer
mov eax, 1 ; system call number (sys_exit)
int 0x80 ; call kernel
integer64btoascii:
; edx:eax = number to convert, edi = buffer to output (at least 20B)
; returns edi pointing after last character
push eax
push edx
push esi
push ecx
mov ch,'1' ; test value for skipping leading zeroes
mov esi,pow10table
.nextPow10: ; [esi+4]:[esi] = 10^k
mov cl,'0'-1
.countPow10: ; subtract 10^k from edx:eax + count it
sub eax,[esi]
sbb edx,[esi+4]
inc cl ; preserves CF
jnc .countPow10
; subtraction overflow, did "one too many" of them
add eax,[esi] ; restore edx:eax to previous value
adc edx,[esi+4]
cmp cl,ch
mov [edi],cl ; write the digit into output
sbb edi,-1 ; advance edi as needed (when cl>=ch)
cmp cl,ch
lea esi,[esi+8] ; next power of 10
adc ch,-1 ; disable zero skip when non-zero found
cmp esi,pow10tableEND
jb .nextPow10 ; until all powers of 10 were processed
cmp ch,'1' ; all zeroes output => edx:eax == 0, CF=0
sbb edi,-1 ; advance edi when CF=0 (all zeroes)
pop ecx
pop esi
pop edx
pop eax
ret
section .rodata
pow10table:
dq 10000000000000000000
dq 1000000000000000000
dq 100000000000000000
dq 10000000000000000
dq 1000000000000000
dq 100000000000000
dq 10000000000000
dq 1000000000000
dq 100000000000
dq 10000000000
dq 1000000000
dq 100000000
dq 10000000
dq 1000000
dq 100000
dq 10000
dq 1000
dq 100
dq 10
dq 1
pow10tableEND:
testnumbers:
dq ~0 ; max 2^64-1 = 18446744073709551615
dq 0 ; looks like zero to me
dd 0, 1 ; 2^32 = 4294967296 (eax = 0)
dq 1234567890 ; < 2^32 (edx = 0)
dq 10000000000000000000 ; largest 10^k to fit into 64b
dq 9999999999999999999 ; to verify "9"
dq 10200300040000500000 ; to verify "0" in-between/at-end
testnumbersEND:
vs ch
冲突而停顿太多(恕我直言不应该,因为价值更新有点分开并且只会发生碰撞很少),然后我相信第二个版本的性能会比第一个版本好,因为分支更加简化了。
但是&#34;相信&#34;这里是关键字,如果你是表演后,个人资料! (并使用&#34;对齐8或16(或者可能只是4)&#34;在关键循环上,验证列表+性能哪一个更好)
还有一个版本,可能更容易理解前导零测试逻辑,而且这可以很容易地扩展到128b整数(cl
和ebx
可以保留,所以它们可以容纳另一个64b输入数字),以及任何其他任意数量的位,当数字+减去在内存中时(因为256b不适合32b x86模式的寄存器)。这也可以修改为在64b模式下工作,只有少量更改的寄存器更多(当然所有这些都需要更大的10 k 功率表,最多为所需位数的最大值)
我发布了这个,因为我对如何解决&#34;零&#34;我非常高兴。最后的谜题 - 这整个周末让我烦恼。
最后,优雅(和更快)的解决方案存在:只需在10 1 幂之后退出减法,因此ebp
留下值eax
。然后只需将该值写入输出而不需要任何&#34;跳过&#34;测试,如果是非零数字,它属于正确的输出,如果输入0-9
,它将创建单个edx:eax == 0
字符。杜!
另外,我设法改变&#34;跳过前导零&#34;生存任意数量的数字的逻辑,不仅仅是48。
'0'
为了获得更好的性能,应该可以通过section .text
global _start ;must be declared for using gcc
_start: ;tell linker entry point
; allocate 24B temporary buffer for ASCII number
sub esp,24
; output test numbers in loop
mov esi,testnumbers
testNumbersLoop:
mov eax,[esi]
mov edx,[esi+4]
mov edi,esp
; call the routine
call integer64btoascii
; add new line to output
mov [edi],byte 10
inc edi
; display number string
mov edx,edi
sub edx,esp ; output length
mov ecx,esp ; output buffer address
mov ebx,1 ; file descriptor (stdout)
mov eax,4 ; system call number (sys_write)
int 0x80 ; call kernel
; loop through test numbers
add esi,8
cmp esi,testnumbersEND
jb testNumbersLoop
; exit
add esp,24 ; release temporary buffer
mov eax, 1 ; system call number (sys_exit)
int 0x80 ; call kernel
integer64btoascii:
; edx:eax = number to convert, edi = buffer to output (at least 20B)
; returns edi pointing after last character
push eax
push edx
push esi
push ecx
mov ch,'0' ; test value to detect leading zeroes
mov esi,pow10table
.nextPow10: ; [esi+4]:[esi] = 10^k
mov cl,'0'-1 ; count of 10^k power in ASCII digit
.countPow10: ; subtract 10^k from edx:eax + count it
sub eax,[esi]
sbb edx,[esi+4]
inc cl ; preserves CF
jnc .countPow10 ; loop till subtraction overflows
; subtraction overflow, did "one too many" of them
or ch,cl ; merge digit into test_leading_zeroes
add eax,[esi] ; restore edx:eax to previous value
adc edx,[esi+4]
cmp ch,'1' ; test is still '0'? => CF=1
mov [edi],cl ; write the digit into output
lea esi,[esi+8] ; next power of 10
sbb edi,-1 ; advance edi as needed (test value > '0')
cmp esi,pow10tableEND
jb .nextPow10 ; until all table powers of 10 were processed
or al,'0' ; remaining eax = 0..9, convert to ASCII
mov [edi],al ; store last digit
inc edi ; last digit will advance edi always
pop ecx
pop esi
pop edx
pop eax
ret
section .rodata
pow10table:
dq 10000000000000000000
dq 1000000000000000000
dq 100000000000000000
dq 10000000000000000
dq 1000000000000000
dq 100000000000000
dq 10000000000000
dq 1000000000000
dq 100000000000
dq 10000000000
dq 1000000000
dq 100000000
dq 10000000
dq 1000000
dq 100000
dq 10000
dq 1000
dq 100
dq 10
pow10tableEND:
testnumbers:
dq ~0 ; max 2^64-1 = 18446744073709551615
dq 0 ; looks like zero to me
dd 0, 1 ; 2^32 = 4294967296 (eax = 0)
dq 1234567890 ; < 2^32 (edx = 0)
dq 10000000000000000000 ; largest 10^k to fit into 64b
dq 9999999999999999999 ; to verify "9"
dq 10200300040000500000 ; to verify "0" in-between/at-end
testnumbersEND:
进行反转值(倒数)乘法以获得div 10
,但这已经超出了我的想法。
这三个版本可能很简单,有些人可以学习大会来理解它们,而且它们可以说明几天内心灵的进步。