2017-05-09 186 views
-5

我在空闲时间学习英特尔汇编语言(att语法),我只是想知道如何将两个数字相乘,让5和2可以不使用mul命令?x86汇编乘以两个32位数

+0

既然你有2的倍数那里,您可以使用左移,右移多次/除以2或2的倍数。 – dawg

+3

嗨,杰克,如果你想在ASM的答案,考虑使用[汇编标签](http://stackoverflow.com/questions/tagged/assembly)而不是[c tag](http://stackoverflow.com/questions/tagged/c)... – Myst

+0

你也可以使用'imul'。或者'aad'。 – fuz

回答

1

除非你的CPU是有缺陷的莫名其妙,你只使用mul命令:-)

然而,在一般意义上,你只需要知道乘法重复此外,让4 x 7为七很多四个加在一起:4 + 4 + 4 + 4 + 4 + 4 + 4

所以对于这样的野兽简单的伪代码将是:

def mul(unsigned a, unsigned b): # line 1 
    res = 0      # line 2 
    while b > 0:     # line 3 
     res = res + a    # line 4 
     b = b - 1     # line 5 
    return res     # line 6 

在样品试运行使用您的测试数据显示了这一过程:

Line# a b res 
----- --- --- --- 
    1 5 2 ? 
    2    0 
    3     (b>0, keep going) 
    4    5 
    5   1 
    3     (b>0, keep going) 
    4    10 
    5   0 
    3     (b==0, exit loop) 
    6     (returns 10) 

请注意,这是使用无符号值,只需稍作修改即可处理带符号的值:

def mul(int a, int b): 
    sign = 1 
    if a < 0: 
     a = -a 
     sign = -sign 
    if b < 0: 
     b = -b 
     sign = -sign 

    res = 0 
    while a > 0: 
     res = res + b 
     a = a - 1 

    if sign == -1: 
     res = -res 
    return res 

此外请记住,实际上有更多的方法可以进行乘法运算,包括值的位移(最小化所需的加法运算),而不是简单的重复加法。

由此我的意思是像9999 x 9999这样的计算将使用简单的方法执行大约10,000次添加。通过使用班次,您可以将其中一个数字所需的添加数限制为每个数字九位,而另一个数字则可以将每位数增加一位,这意味着您可以在上面的计算中添加约40个添加项。

希望这将让感觉,当你意识到你可以简化9999 x 9999到:

 9999 x 9 -> nine additions 
+ 99990 x 9 -> nine additions 
+ 999900 x 9 -> nine additions 
+ 9999000 x 9 -> nine additions 
       \____________/ 
         | 
         V 
       three additions 

如果你想看到更详细换挡作品,维基百科有一个article on the topic


顺便说一句,你可以乘以常数时,因为你提前知道行动需要进行哪些得到相当不错的性能。例如,十乘以寄存器可以用像做(请记住我的组装日子已经过去长):

mul_ax_by_10: push bx  ; save registers 
       shl ax  ; ax <- orig_ax * 2 
       push ax  ; save for later add 
       shl ax 
       shl ax  ; ax <- orig_ax * 8 
       pop bx  ; bx <- orig_ax * 2 
       add ax, bx ; ax <- (orig_ax * 8) + (orig_ax * 2) 
          ; <- orig_ax * (8 + 2) 
          ; <- orig_ax * 10 
       pop bx  ; restore saved register 
       ret   ; result in ax 
+0

是的,我知道如何用c代码写出来,但我不知道如何在装配中做到这一点。 – jack

+0

你能举个例子说明如何用shift命令乘两个数字。假设我们存储5中的a和10中的b,当我们移位时,5和10将以二进制表示。 – jack

+0

@jack:请参阅https://en.wikipedia.org/wiki/Multiplication_algorithm#Shift_and_add –

0

由于您所标记的问题与,我假设你疲于应付GCC的内联汇编程序。

在旧的十进制日期中,您进行了乘法 - 例如, 11 * 14 - 如下:

  1. 你把乘数(11)= 1的最右边的数字,并与被乘数乘以它(14)= 14

  2. 你拍了下位左乘数= 1,乘以乘数= 14并将结果小数点左移一位数= 140.您也可以移位被乘数而不是结果:1 * 140 = 140。

  3. 您加入的结果,最终的结果是:14 + 140 = 154

这种算法也是binarism的勇敢新世界有效:

  1. 就拿最右边(乘法器)(1110b)乘以乘法器(1011b)的乘法运算结果。这不是一个真正的乘法。您只有两个选项:0 * 1110b = 0和1 * 1110b = 1110b。结果是根据位0或被乘数。将其添加到最终结果。

  2. 如果乘数大于零,则下一位等待乘数中最右边的位。移位被乘数由一个比特的左(在以上步骤2中的第二个选项)和转到步骤1.

的GCC程序:

#include <stdio.h> 

unsigned mul (unsigned multiplier, unsigned multiplicand) 
{ 
    unsigned r = 0; 
    while (multiplier) 
    { 
     if (multiplier & 1) r += multiplicand; 
     multiplier >>= 1; 
     multiplicand <<= 1; 
    } 
    return r; 
} 

unsigned mul_asm (unsigned multiplier, unsigned multiplicand) 
{ 
    unsigned result = 0; 

    asm 
    (
     "movl %[multiplier], %%edx;"  // EDX = multiplier 
     "movl %[multiplicand], %%ecx;"  // ECX = multiplicand 
     "xorl %%eax, %%eax;"    // Result = 0 

     "L1:;"        // While-loop 
     "shrl $1, %%edx;"     // Get rightmost bit of the multiplier 
     "jnc 1f;"       // Skip the next line if this bit == 0 
     "leal (%%ecx,%%eax), %%eax;"  // Add multiplicand to result (without changing flags) 
     "1:;"        // Local jump label 
     "leal (,%%ecx,2), %%ecx;"   // Shift multiplicand left by one bit (without changing flags) 
     "jnz L1;"       // While EDX != 0 (zero flag from the shrl-line) 

     "movl %%eax, %[result];"   // Return value 

     : [result] "=m" (result)   // Output 
     : [multiplier] "m" (multiplier), // Input 
      [multiplicand] "m" (multiplicand) 
     : "%eax", "%ecx", "%edx", "cc"  // Clobbered registers & flags 
    ); 

    return result; 
} 

int main (void) 
{ 
    unsigned result, multiplier, multiplicand; 

    multiplier = 17; 
    multiplicand = 23; 

    result = multiplier * multiplicand; 
    printf ("direct: %u * %u = %u\n", multiplier, multiplicand, result); 

    result = mul (multiplier,multiplicand); 
    printf ("mul:  %u * %u = %u\n", multiplier, multiplicand, result); 

    result = mul_asm (multiplier,multiplicand); 
    printf ("mul_asm: %u * %u = %u\n", multiplier, multiplicand, result); 

    return 0; 
}