汇编中的平方根,如何移位和更改位

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 延迟方式的结果,如果你小心不要修改所需的标志)。