2017-05-09 168 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; 
}