x86:计数从32位数转换为1到0

时间:2016-05-11 18:12:33

标签: assembly x86 nasm

我喜欢在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。

2 个答案:

答案 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.