我喜欢在32位数字中计算1到0的转换次数。我发现使用shr
以相反顺序从0到1的转换次数,但是我没有得到所需的结果。我该怎么办?
extern printf
SECTION .data
msg: db "The number of transitions are : %d",10,0
inta1: dd 1234567 ; integer 1234567
num: dd 4660
SECTION .text
global main
main:
mov eax, [num]
mov ecx,32
mov ebx,0
.loop: dec ecx
cmp ecx,0
jl .exit
shr eax,1
jc .loop
dec ecx
shr eax, 1
jnc .loop
inc ebx
jmp .loop
.exit:
push ebx
push dword msg
call printf
add esp, 8
输出:
转换次数为:2
而对于4660(00000000000000000001001000110100
),1到0转换的数量为4。
答案 0 :(得分:4)
基本问题是,您的代码会查找0
后跟1
,如果它不匹配,则会重新开始。因此,如果它看到00
,它会重新开始寻找另一个0
,如果下一位是1
则重新开始。因此,当它们在偶数0
s
另请注意,您说您想要1到0的转换,但是您正在寻找0到1的转换, BUT 您正在向右看(lsb到msb)这可能是正确的,具体取决于你想要的位顺序。
显而易见的解决方案是将jnc .loop
更改为不重新开始,但只返回前一个dec ecx
,但您还需要cmp ecx,0; jl .exit
。
另请注意,您可以摆脱ecx
的需要,并使用Z
标志计算位数,该标志由shr
指令设置(eax
{1}})全是0位。
总而言之,这给你的东西:
.loop1: shr eax, 1
jc .loop1
.loop2: jz .exit
shr eax, 1
jnc .loop2
inc ebx
jmp .loop1
答案 1 :(得分:3)
Trivia:我在尝试实现基于位图的内存管理器的一个方面时,在几个月前考虑过这个问题。
我找到的最有效的解决方案如下(我将其改编为您的示例代码):
global main
main:
mov eax, [num]
; --- here starts the relevant code ---
mov ebx, eax
shl ebx, 1
not eax
and ebx, eax
popcnt ebx, ebx ; --- count the changed bits ---
; --- continue with your exit routine
.exit:
push ebx
push dword msg
call printf
add esp, 8
请注意,计算是在没有循环的情况下实现的。通过POPCNT
指令最终对设置位进行计数,得到所需的结果,这是纯粹的比特。
作为示例 - 应用于给定值4660,此算法的功能如下:
00000000000000000001001000110100 --- MOV EBX, EAX
00000000000000000010010001101000 --- SHL EBX, 1
11111111111111111110110111001011 --- NOT EAX
00000000000000000010010001001000 --- AND EBX, EAX => 'and' the last two lines
=> POPCNT (count the bits of the last line) to get the result of 1 to 0 transitions.