【问题标题】:x86: Count transitions from 1 to 0 in 32 bit numberx86:以 32 位数计算从 1 到 0 的转换
【发布时间】:2016-05-11 18:12:32
【问题描述】:

我喜欢计算 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

标签: assembly x86 nasm


【解决方案1】:

基本问题是您的代码查找0 后跟1,如果不匹配,则重新开始。因此,如果它看到00,它就会重新开始寻找另一个0,如果下一位是1,它就会重新开始。因此,当它们前面有偶数个 0s 时,你会错过转换

还请注意,您说您想要 1 到 0 转换,但您正在寻找 0 到 1 转换,但是您正在寻找从右到左(lsb 到 msb)以便可能是正确的,具体取决于您想要的位顺序。

显而易见的解决方案是将jnc .loop 更改为不重新开始,而只返回到紧接前面的dec ecx,尽管您还需要一个cmp ecx,0; jl .exit

还请注意,当结果(在eax 中)全为 0 时,您可以通过使用由shr 指令设置的Z 标志来摆脱对ecx 的需要并计算位数位。

综合起来,你会得到类似的东西:

.loop1: shr eax, 1
        jc .loop1
.loop2: jz .exit
        shr eax, 1
        jnc .loop2
        inc ebx
        jmp .loop1

【讨论】:

  • 最后一个jmp .loop 应该是jmp .loop1(无论如何+1)。
  • 您可以在底部使用jnz 构建循环,然后使用test eax,eax / jmp loop_entry_point 输入它。然后你会在循环中少一条指令(没有无条件分支)。另请注意,您可以使用adc ebx, 0CF 添加到ebx。或setc dl/add bl, dl。无论哪种方式,您都可以避免在移出位时出现条件分支,因为这可能无法很好地预测。
【解决方案2】:

琐事:一个月前,我在尝试实现基于位图的内存管理器的一个方面时想到了这个问题。

我找到的最有效的解决方案如下(我根据您的示例代码对其进行了调整):

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.

【讨论】:

  • @SaumyaSahay,如果你必须在书和popcnt之间做出选择,你应该选择popcnt(书已经过时了)。
  • @Saumya Sahay:是的,如果您手动实现一个例程来计算DWORD 中的位,则可以在没有POPCNT 的情况下完成。这已经完成并且非常烦人,但幸运的是,几乎所有当前的处理器多年来都支持POPCNT。它被介绍了by AMD with Barcelona architecture (SSE4a) in 2007 and by Intel with Nehalem architecture in 2008。恕我直言,这可能被认为是处理器功能的合适年龄。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多