Assembly 如何计算十进制数中有多少位是1?

Assembly 如何计算十进制数中有多少位是1?,assembly,riscv,hammingweight,population-count,Assembly,Riscv,Hammingweight,Population Count,我在RISC-V RARS 1.3应用程序中创建的这个程序旨在获取一个十进制数字,并计算该数字中的位数。我正在测试的是十进制数5,这个程序应该适用于我在t1上输入的任何正数。这是我创建的代码。当AND函数的结果不是0时,该程序将添加一个计数器,但我遇到的问题是程序没有停止。这个问题有解决办法吗 _start: li t1,2 # start with decimal 5, binary 101 li t2,1 # adding counter for AND function li t3,0

我在RISC-V RARS 1.3应用程序中创建的这个程序旨在获取一个十进制数字,并计算该数字中的位数。我正在测试的是十进制数5,这个程序应该适用于我在t1上输入的任何正数。这是我创建的代码。当AND函数的结果不是0时,该程序将添加一个计数器,但我遇到的问题是程序没有停止。这个问题有解决办法吗

_start:

li t1,2 # start with decimal 5, binary 101
li t2,1 # adding counter for AND function
li t3,0 # bit counter count
li t4,0 # to compare 0

and t5,t1,t2 # t1 & t2 = t5
bne t5,t4,label # go to label if t5 != 0
beqz t5,label2 # go to label if t5 == 0

label:
addi t3,t3,1 # add one to bit count
slli t2,t2,1 # shift left
and t5,t1,t2 # t1 & new t2 = t5
bne t5,t4,label # go to label if t5 != 0
beqz t5,label2 # go to label if t5 == 0

label2:
slli t2,t2,1 # shift left
and t5,t1,t2 # t1 & new t2 = t5

.data

由于您从
t2=1开始,并在每次迭代中将其乘以
2
,因此当
t2
的值大于
t1
时,应停止计算

另外,在您的代码中,我看到您可能打算处理两种情况:

  • label:
    -此块处理当前测试位为1时的情况,它增加位数,然后跳到
    label
    label2
    。在这里,您只需要添加上面提到的退出条件
  • label2:
    -此块处理当前测试位为0时的情况,它不会更改位的数量,但它似乎也不会继续使用
    label
    label2
    。我认为应该继续查看是否有更高的1位,直到达到退出条件
    t2>t1

  • 由于您从
    t2=1开始,并在每次迭代中将其乘以
    2
    ,因此当
    t2
    的值大于
    t1
    时,应停止计算

    另外,在您的代码中,我看到您可能打算处理两种情况:

  • label:
    -此块处理当前测试位为1时的情况,它增加位数,然后跳到
    label
    label2
    。在这里,您只需要添加上面提到的退出条件
  • label2:
    -此块处理当前测试位为0时的情况,它不会更改位的数量,但它似乎也不会继续使用
    label
    label2
    。我认为应该继续查看是否有更高的1位,直到达到退出条件
    t2>t1

  • 如果RISC-V没有有效计算设置位数的指令(大多数其他CPU现在都有);接下来最好的方法是:

        // Value is 32 pieces with 1 bit per piece
    
        temp1 = (value & 0x555555555) + (value & 0xAAAAAAAA) >> 1;
    
        // Temp1 is 16 pieces with 2 bits per piece
    
        temp2 = (temp1 & 0x33333333) + (temp1 & 0xCCCCCCCC) >> 2;
    
        // Temp2 is 8 pieces with 4 bits per piece
    
        temp3 = (temp2 & 0x0F0F0F0F) + (temp2 & 0xF0F0F0F0) >> 4;
    
        // Temp3 is 4 pieces with 8 bits per piece
    
        temp4 = (temp3 & 0x00FF00FF) + (temp3 & 0xFF00FF00) >> 8;
    
        // Temp4 is 2 pieces with 16 bits per piece
    
        result = (temp4 & 0x0000FFFF) + (temp2 & 0xFFFF0000) >> 16;
    
        // Result is the count of all bits that were set in value (or, sum of all 32 of the original 1-bit values)
    

    我不知道如何在RISC-V汇编中编写它(我会编译它,然后剪切并粘贴生成的汇编,但也没有RISC-V的编译器)。

    如果RISC-V没有有效计算设置位数的指令(大多数其他CPU现在都有);接下来最好的方法是:

        // Value is 32 pieces with 1 bit per piece
    
        temp1 = (value & 0x555555555) + (value & 0xAAAAAAAA) >> 1;
    
        // Temp1 is 16 pieces with 2 bits per piece
    
        temp2 = (temp1 & 0x33333333) + (temp1 & 0xCCCCCCCC) >> 2;
    
        // Temp2 is 8 pieces with 4 bits per piece
    
        temp3 = (temp2 & 0x0F0F0F0F) + (temp2 & 0xF0F0F0F0) >> 4;
    
        // Temp3 is 4 pieces with 8 bits per piece
    
        temp4 = (temp3 & 0x00FF00FF) + (temp3 & 0xFF00FF00) >> 8;
    
        // Temp4 is 2 pieces with 16 bits per piece
    
        result = (temp4 & 0x0000FFFF) + (temp2 & 0xFFFF0000) >> 16;
    
        // Result is the count of all bits that were set in value (or, sum of all 32 of the original 1-bit values)
    

    我不知道如何在RISC-V汇编中编写这个程序集(我会编译它,然后剪切并粘贴生成的程序集,但也没有RISC-V编译器)。

    你能先在C中编写吗?当你们还不懂汇编语言的时候,在汇编语言中开发算法就困难多了。我推荐使用C,因为您可以通过一些简单的测试来验证C版本的有效性(在汇编级别调试时,翻译一个被破坏的算法可能会非常痛苦)。一旦你有了一个正常工作的C算法,然后按字面意思将其翻译成汇编,变量对变量,代码行对代码行,而不需要在翻译过程中进行任何改进。
    li t1,2#从十进制5开始
    -不,
    li t1,2
    将十进制数
    2
    放入寄存器,
    0…00010
    。显然,您的算法也被破坏了,但当代码中的第一件事情与注释不匹配时,这不是一个好的开始。RISC-V的寄存器x0始终为零。无需将
    0
    加载到另一个寄存器中进行比较。您也不需要
    bne
    beqz
    ,只需要
    beqz label2
    或陷入循环。您可以先用C编写吗?当你们还不懂汇编语言的时候,在汇编语言中开发算法就困难多了。我推荐使用C,因为您可以通过一些简单的测试来验证C版本的有效性(在汇编级别调试时,翻译一个被破坏的算法可能会非常痛苦)。一旦你有了一个正常工作的C算法,然后按字面意思将其翻译成汇编,变量对变量,代码行对代码行,而不需要在翻译过程中进行任何改进。
    li t1,2#从十进制5开始
    -不,
    li t1,2
    将十进制数
    2
    放入寄存器,
    0…00010
    。显然,您的算法也被破坏了,但当代码中的第一件事情与注释不匹配时,这不是一个好的开始。RISC-V的寄存器x0始终为零。无需将
    0
    加载到另一个寄存器中进行比较。您也不需要
    bne
    beqz
    ,只需要
    beqz label2
    或陷入循环。在具有快速乘法的机器上,您可以通过一次乘法和右移,对32或64位整数中的字节进行水平求和,这也优化了第一步,即使用
    sub
    ,并且只屏蔽一次,使用
    i=i-((i>>1)和0x5555)。对于以后的操作,每一步仍然只需要一个掩码(RISC的一大优势是,每个32位值必须用2条指令单独构造)。掩蔽前的移位,如
    t2=(t1&0x33..33)+((t1>>2)和0x33..33)
    。与x86或AArch64不同,您不能仅将每个掩码用作AND指令的立即数。即使使用移位从另一个掩码构造一个掩码,也会花费额外的指令,因为在添加之前,您仍然必须右移位结果。在具有快速乘法的计算机上,您可以使用一个乘法和一个右移位对32或64位整数中的字节进行水平求和,如中所述,这也优化了使用
    sub