x86汇编乘以两个32位数
除非你的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
由于您所标记的问题与c,我假设你疲于应付GCC的内联汇编程序。
在旧的十进制日期中,您进行了乘法 - 例如, 11 * 14 - 如下:
你把乘数(11)= 1的最右边的数字,并与被乘数乘以它(14)= 14
你拍了下位左乘数= 1,乘以乘数= 14并将结果小数点左移一位数= 140.您也可以移位被乘数而不是结果:1 * 140 = 140。
您加入的结果,最终的结果是:14 + 140 = 154
这种算法也是binarism的勇敢新世界有效:
就拿最右边(乘法器)(1110b)乘以乘法器(1011b)的乘法运算结果位。这不是一个真正的乘法。您只有两个选项:0 * 1110b = 0和1 * 1110b = 1110b。结果是根据位0或被乘数。将其添加到最终结果。
如果乘数大于零,则下一位等待乘数中最右边的位。移位被乘数由一个比特的左(在以上步骤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;
}
既然你有2的倍数那里,您可以使用左移,右移多次/除以2或2的倍数。 – dawg
嗨,杰克,如果你想在ASM的答案,考虑使用[汇编标签](http://*.com/questions/tagged/assembly)而不是[c tag](http://*.com/questions/tagged/c)... – Myst
你也可以使用'imul'。或者'aad'。 – fuz