汇编中的平方根,如何移位和更改位
Square root in assembly, how to shift and change bits
我想用汇编写一个快速的整数平方根算法,它需要无符号的 32 位。我一直在通读 this,并有了一个想法。这是我的伪代码:
res <- 0
for i from 15 downto 0 do:
change the ith bit of result to 1
if res^2 > x then:
change the ith bit of res back to 0
return res
到目前为止我已经完成了:
sqrt:
movl [=11=], %eax
movl , %edx
jmp .L8
.L9
.L8
cmpq cmpq [=11=], %edx
jge .L9
我卡在 for 循环操作中,更改第 i 位并移位。我也不想使用除法或 sqrt 指令。我知道我应该使用 shr
,但我不知道从哪里开始或如何做。如何在 for 循环中执行操作?我从哪里开始?
(Intel语法,自行转AT&T)
mov ebx,<number> ; *number* to find sqrt of
mov ecx,0x8000 ; bitmask (starting with b15 bit set)
;^^^ 0x8000 = decimal 32768 = binary 1000 0000 0000 0000
xor eax,eax ; result <- 0
sqrt_loop:
xor eax,ecx ; set bit in eax
push eax ; store result (will be destroyed by mul)
mul eax ; edx:eax <- eax*eax (ignoring edx next)
cmp eax,ebx ; compare with *number*
pop eax ; restore result
jbe keep_bit ; res^2 <= *number* -> bit stays set
xor eax,ecx ; unset bit in eax
keep_bit:
shr ecx,1 ; next bit
jnz sqrt_loop ; loop till all bits are tried
(我没有尝试+调试它,所以可能有一些错误。但我认为加上你的伪算法和你通过调试重写到 AT&T 应该足以让你开始)
正如玛格丽特指出的那样,数字就是数字,它就是价值。因此 0x8000 已经在 CPU 线路中编码为 b15 设置为 1,其他位设置为 0。当您想要转换值 from/into 字符串时,所有转换都会发生,但只要您正在计算有了价值,它同时以各种形式存在于寄存器中。这仅取决于您如何看待寄存器。在源代码中使用 hexa/decimal/binary 是,编写数字的 STRING 表示,由汇编器将其转换为值本身。
二进制表示是特殊的,因为 CPU 可以寻址特定位(使用 and/xor/or、旋转、位 test/set 等),因为它具有这些值"wires" 并且它是它的本地表示。就像人类在计算“10*3456”时是"cheating"一样,在末尾加0就可以得到结果,因为在十进制格式中这就是10*的特殊之处。对于 CPU ,位操作和 2 数学的各种幂也会发生同样的情况。但是小数技巧是不可能的,那些有 CPU 以正确的方式计算,真正乘以 10。
无论如何,当你只有位号时,你想得到位掩码本身,比如如何从 15 得到 0x8000:
mov ecx,15 ; i-th bit
mov eax,1 ; set b0 (lowest bit)
shl eax,cl ; shift all bits (all zeroed + b0 set) cl-many times left
; eax now contains 0x8000 = b15 set, other bits zeroed
因此,如果您坚持自己的算法方式,则每次都必须重新计算 for 计数器到位掩码(或使用一些位 set/reset 指令,我从头上不知道,因为几乎不需要它们)。
但是如果你研究我的代码,你会发现有直接的快捷方式来处理位掩码本身,不计算 "i-th bit" 部分,使代码更短更快(尽管我可能被那个 push/pop,也许再使用一个像 esi
这样的寄存器来存储值会更好......然后这再次演示了如何使用堆栈,以及标志如何不受某些指令的影响,所以你可以使用 cmp
延迟方式的结果,如果你小心不要修改所需的标志)。
我想用汇编写一个快速的整数平方根算法,它需要无符号的 32 位。我一直在通读 this,并有了一个想法。这是我的伪代码:
res <- 0
for i from 15 downto 0 do:
change the ith bit of result to 1
if res^2 > x then:
change the ith bit of res back to 0
return res
到目前为止我已经完成了:
sqrt:
movl [=11=], %eax
movl , %edx
jmp .L8
.L9
.L8
cmpq cmpq [=11=], %edx
jge .L9
我卡在 for 循环操作中,更改第 i 位并移位。我也不想使用除法或 sqrt 指令。我知道我应该使用 shr
,但我不知道从哪里开始或如何做。如何在 for 循环中执行操作?我从哪里开始?
(Intel语法,自行转AT&T)
mov ebx,<number> ; *number* to find sqrt of
mov ecx,0x8000 ; bitmask (starting with b15 bit set)
;^^^ 0x8000 = decimal 32768 = binary 1000 0000 0000 0000
xor eax,eax ; result <- 0
sqrt_loop:
xor eax,ecx ; set bit in eax
push eax ; store result (will be destroyed by mul)
mul eax ; edx:eax <- eax*eax (ignoring edx next)
cmp eax,ebx ; compare with *number*
pop eax ; restore result
jbe keep_bit ; res^2 <= *number* -> bit stays set
xor eax,ecx ; unset bit in eax
keep_bit:
shr ecx,1 ; next bit
jnz sqrt_loop ; loop till all bits are tried
(我没有尝试+调试它,所以可能有一些错误。但我认为加上你的伪算法和你通过调试重写到 AT&T 应该足以让你开始)
正如玛格丽特指出的那样,数字就是数字,它就是价值。因此 0x8000 已经在 CPU 线路中编码为 b15 设置为 1,其他位设置为 0。当您想要转换值 from/into 字符串时,所有转换都会发生,但只要您正在计算有了价值,它同时以各种形式存在于寄存器中。这仅取决于您如何看待寄存器。在源代码中使用 hexa/decimal/binary 是,编写数字的 STRING 表示,由汇编器将其转换为值本身。
二进制表示是特殊的,因为 CPU 可以寻址特定位(使用 and/xor/or、旋转、位 test/set 等),因为它具有这些值"wires" 并且它是它的本地表示。就像人类在计算“10*3456”时是"cheating"一样,在末尾加0就可以得到结果,因为在十进制格式中这就是10*的特殊之处。对于 CPU ,位操作和 2 数学的各种幂也会发生同样的情况。但是小数技巧是不可能的,那些有 CPU 以正确的方式计算,真正乘以 10。
无论如何,当你只有位号时,你想得到位掩码本身,比如如何从 15 得到 0x8000:
mov ecx,15 ; i-th bit
mov eax,1 ; set b0 (lowest bit)
shl eax,cl ; shift all bits (all zeroed + b0 set) cl-many times left
; eax now contains 0x8000 = b15 set, other bits zeroed
因此,如果您坚持自己的算法方式,则每次都必须重新计算 for 计数器到位掩码(或使用一些位 set/reset 指令,我从头上不知道,因为几乎不需要它们)。
但是如果你研究我的代码,你会发现有直接的快捷方式来处理位掩码本身,不计算 "i-th bit" 部分,使代码更短更快(尽管我可能被那个 push/pop,也许再使用一个像 esi
这样的寄存器来存储值会更好......然后这再次演示了如何使用堆栈,以及标志如何不受某些指令的影响,所以你可以使用 cmp
延迟方式的结果,如果你小心不要修改所需的标志)。