当前位置: 代码迷 >> 汇编语言 >> 只使用移位和加法指令实现两个32位无符号整数相乘的难题解决方法
  详细解决方案

只使用移位和加法指令实现两个32位无符号整数相乘的难题解决方法

热度:7662   发布时间:2013-02-26 00:00:00.0
只使用移位和加法指令实现两个32位无符号整数相乘的难题
《Intel   汇编语言程序设计》第七章的一道编程题

如果乘积不超过32位这个过程很容易写。

但是如果要实现mul一样的功能(即乘积高32位放到edx中)就麻烦了,

假设被乘数eax为10h,乘数为12345678h,当shl   eax,28的时候值溢出了,我们怎样把这个溢出了的数值弄到edx去呢?如果eax为100h,那么逻辑左移24位就溢出,这个移位数也不好控制。

下面是我的代码:
===================================
;参数     :ebx为乘数,eax为被乘数
;返回值:edx为结果高32位,eax为结果低32位。
push   ebx
push   ecx
push   esi
push   edi
mov   edi,ebx
mov   esi,eax
mov   eax,0
mov   edx,0
mov   bl,0
mov   ecx,32
clc
L1:
push   ecx
shr   edi,1
.if   Carry?
mov   cl,bl
push   esi
shl   esi,cl
add   eax,esi
adc   edx,0
pop   esi
.endif

inc   bl
pop   ecx
loop   L1

pop   edi
pop   esi
pop   ecx
pop   ebx
ret  
===================================

------解决方案--------------------------------------------------------
假设被乘数eax为10h,乘数为12345678h,当shl eax,28的时候值溢出了,我们怎样把这个溢出了的数值弄到edx去呢?如果eax为100h,那么逻辑左移24位就溢出,这个移位数也不好控制。

看开码不如看算法,所以我提供算法,如下:
1.首先,谁乘谁都是一样的,所以我们用12345678H作为被乘数,用10H作为乘数,这样做可以减少移位次数。
2.因为要使用EDX,所以先对其进行清零操作。
3.乘数12345678H,显然是32位,放入EAX,被乘数10H即十进制数16,二进制数是10000B,占用5个二进制位。把4放入CX中。用CX控制循环次数,每次移动一个二进制位,用带进位的非循环左移指令,左移EAX一次,再用进带进位的非循环左移移动EDX一次,如此循环5次就可以完成12345678H*10H这个计算。

特别说明:如果乘数是100H,也就是十进制数的256,折成二进制位是8位,因此只要移动8次就可以了。我们移动的时候要根据乘数计算要移动的位数,要找一个算法计算出要移位的位数。如果要移位的位数是2的n次幂,则很容易计算,用循环右移,移到目标源操作数为1为止,如果乘数是6,也就是说,左移两位不够,左移3位又移多了,我们就要将6拆分成4和2,左移2位,和左移1位,然后采用相加的办法。
------解决方案--------------------------------------------------------
64位乘法啊,也来一段代码(变量使用伪码表示,稍后解释):

_allmul PROC NEAR

mov eax,HIDOWRD(A)
mov ecx,HIDOWRD(B)
or ecx,eax ;检测高32位是否为0
mov ecx,LODWORD(B)
jnz SHORT hard ;为0则只需要进行32位乘法
mov eax,LOWDWORD(A)
mul ecx
ret

hard:
push ebx
mul ecx ;高32位相乘
mov ebx,eax ;保存结果
mov eax,LOWDWORD(A2)
mul DWORD PTR HIDWORD(B2)
add ebx,eax ;ebx=((A的低32位*B的高32位)+(A的高32位+B的低32位))
mov eax,LOWDWORD(A2) ;ecx=B的低32位
mul ecx ;edx:eax=A的低32位*B的低32位
add edx,ebx ;edx即为所有高低位之和
pop ebx
ret

_allmul ENDP

HIDWORD(A)代表被乘数A的高32位LOWDOWRD(B)代表乘数B的低32位,其他以此类推。
这段代码反汇编自Microsoft C run-time library,也是编译器处理64位乘法的标准方案。
------解决方案--------------------------------------------------------
这样写代码可能简单一点:

LOCALS @@
SMUL PROC NEAR
PUSH ESI
PUSH EDI
PUSH 0
PUSH 0
MOV ECX,31
@@1:
MOV EDI,1
SHL EDI,CL
TEST EDX,EDI
JZ @@2
XOR ESI,ESI
MOV EDI,EAX
SHLD ESI,EDI,CL
SHL EDI,CL
ADD [ESP],EDI
ADC [ESP+4],ESI
@@2:
LOOP @@1
POP EAX
POP EDX
POP EDI
POP ESI
RET
SMUL ENDP

输入:EAX-被乘数,EDX-乘数
输出:EDX:EAX-64位结果,高32位在EDX中
  相关解决方案