大数乘法(汇编语言)

参考

寒士的文章

大数乘法的模式还是很僵硬的,就那个流程,具体的细节看个人如何优化。

大数乘法(汇编语言)_第1张图片

实验内容

实现两个十进制大整数的相乘(100位以上),输出乘法运算的结果。

实验环境

Microsoft Visual Studio 2017+masm 32

思路

  1. 读取:获取输入字符串×2
  2. 预处理(pre_proc):将字符串翻转,转换进制。这一步可以由栈一次性完成
    • 判断正负,计算长度
    • 循环,入栈,入栈前减去30H(字符’0’)
    • 循环,出栈
  3. 模拟手算乘法(high_multiply):
    • 直接乘。x的第i位与y的第j位相乘时,结果存储到结果的第i+j位
    • 进位。当前位=数字/10,下一位=数字 mod 10
    • 计算结果长度。
  4. 结果处理(int2str_reverse):结果逆向翻转,与预处理函数相反
  5. 带符号输出

代码

.386
.model flat, stdcall
option casemap : none

includelib msvcrt.lib
include msvcrt.inc

endl equ <0DH,0AH>
.data
;numcharA/B最多容纳100个字节的字符,全部初始化为0
numCharA byte 100 dup(0)
numCharB byte 100 dup(0)
resultChar byte 200 dup(0)
numIntA dword 100 dup(0)
numIntB dword 100 dup(0)
resultInt dword 200 dup(0)
;输入的格式
inputMsg byte "请输入数字: ",0
szFmt_s byte "%s", 0
szFmt_d byte "%d", 0
;输出的格式
testCharMsg byte "%c",endl,0
testNumMsg byte "%d",endl,0
outputMsg byte "结果是:%s",endl,0
outputMsg2 byte "结果是:%s%s",endl,0
;把lenthA/B/C初始化为0
lengthA dword 0
lengthB dword 0
lengthC dword 0
;符号标志位,初始值为0,表示为正,为1表示为负	
radix dword 10
negativeFlag byte 0
negativeImg byte "-"

.code
;=================================================================================
;字符串中的内容依次入栈,然后出栈,实现反转功能,并将byte型数组转化为dword型数组
;反转前为numChar,为byte型数组,每个byte存储字符'0'~'9',反转后为numInt,为dword型数组,每个dword存储数字0~9
str2int_reverse proc far C uses eax ecx esi,numChar:ptr byte, numInt:ptr dword, len:dword
	;顺次入栈,入栈前-'0'
	mov ecx, len ;字符串长度即为循环的次数
	mov esi, numChar
L1:
	movzx eax, byte ptr [esi]
	sub eax, 30H 
	push eax 
	inc esi 
	loop L1 
	;逆序出栈
	mov ecx, len
	mov esi, numInt
L2:
	pop eax
	mov dword ptr [esi], eax
	add esi, 4
	loop L2	

	ret
str2int_reverse endp


;===============================================================
;是上一个函数的逆过程
int2str_reverse proc far C uses eax ecx esi,numChar:ptr byte, numInt:ptr dword, len:dword
	;顺序入栈
	mov ecx, len ;结果的长度为循环的次数
	mov esi, numInt
L1:
	mov eax, dword ptr [esi] 
	add eax, 30H 
	push eax
	add esi,4
	loop L1
	;逆序出栈
	mov ecx, len
	mov esi, numChar
L2:
	pop eax
	mov byte ptr [esi],al ;依次出栈,把低八位存在resultChar的对应位置中,最低位先出栈,存在resultChar的最低位中
	inc esi
	loop L2
	
	ret
int2str_reverse endp


;=======================================================================================================
;计算带符号的数字长度,并且调用翻转函数
;ptr byte型的参数,用法很奇怪,建议送到寄存器里用。
pre_proc proc far C uses eax ebx esi,numCharX:ptr byte,numIntX:ptr dword,len:ptr dword
	mov ebx,numCharX
	mov esi,len
	;invoke crt_printf,addr testNumMsg,addr lengthB
	;invoke crt_printf,addr testNumMsg,len
	;invoke crt_printf,addr testNumMsg,esi
	;invoke crt_printf,addr testCharMsg,numCharB
	;invoke crt_printf,addr testCharMsg,byte ptr [ebx]
	.if byte ptr[ebx] == 2DH	;2DH对应ASCII码为'-'
		xor negativeFlag, 1 ;负号作用一次
		inc ebx	;指针+1,跳过符号位
	.endif
	invoke crt_strlen, ebx ;跳过负号求字符串长度,结果在eax中
	mov dword ptr [esi], eax
	invoke str2int_reverse, ebx, numIntX,dword ptr [esi] ;字符串反转,且byte数组转dword数组
	ret
pre_proc endp

;=============================================================================================================
;模拟手算的大数相乘算法
high_multiply proc far C uses eax ecx esi ebx
	mov ebx, -1
OuterLoop: 
	inc ebx
	cmp ebx, lengthA
	jnb endLoop1 ;如果ebx >= lengthA,结束循环
	xor ecx, ecx
InnerLoop:
	xor edx, edx
	mov eax, dword ptr numIntA[4 * ebx]
	mul numIntB[4 * ecx] ;numIntA[4 * ebx] * numIntB[4 * ecx]结果放在EDX:EAX中,最大9*9 = 81也不会超过8个字节,所以结果只在EAX中
	mov esi, ecx
	add esi, ebx ;esi = ecx + ebx,即两个下标之和
	add resultInt[4 * esi], eax ;把两个位相乘的结果加到resultInt的相应位上
	inc ecx
	cmp ecx, lengthB 
	jnb OuterLoop ;无符号数ecx>=lengthB时,下标超过lengthB - 1时跳出内层循环重新进行外层循环
	jmp InnerLoop	;不超过则继续进行内层循环
endLoop1:
	mov ecx, lengthA
	add ecx, lengthB
	inc ecx	;ecx = lengthA + lengthB + 1
	mov esi, offset lengthC
	mov [esi], ecx ;将ecx赋给lengthC

	xor ebx, ebx
CarryCul:
	cmp ebx, ecx
	jnb endLoop2 ;ebx >= ecx跳到endLoop2,跳出求进位的循环
	mov eax, resultInt[4  * ebx]
	xor edx, edx
	div radix
	add resultInt[4 * ebx + 4], eax ;resultInt[i+1] += resultInt[i]/10
	mov resultInt[4 * ebx], edx ;resultInt[i] = resultInt[i] % 10
	inc ebx
	jmp CarryCul
endLoop2: 
	mov ecx, lengthC ;让MoveZero从最后一位开始检查
MoveZero:
	cmp dword ptr resultInt[4 * ecx], 0
	jnz endwhile1 ;resultInt的末位不为0
	dec ecx ;每检测到一个0,实际长度减一 
	jmp MoveZero
endwhile1:
	inc ecx ;实际长度为最大下标加一
	mov esi, offset lengthC
	mov [esi], ecx ;将ecx赋给lengthC

	ret
high_multiply endp

;======================================================================================
;主函数
main proc
	;键盘分别输入A和B,并存储为byte数组
	invoke crt_printf, addr inputMsg
	invoke crt_scanf, addr szFmt_s, addr numCharA 
	invoke crt_printf, addr inputMsg
	invoke crt_scanf, addr szFmt_s, addr numCharB
	;预处理,求长度并且翻转为数字数组
	invoke pre_proc,addr numCharA,addr numIntA,addr lengthA
	invoke pre_proc, addr numCharB,addr numIntB,addr lengthB
	;从低位到高位计算,结果送到resultInt
	invoke high_multiply
	;将resultInt逆序翻转为resultChar
	invoke int2str_reverse,addr resultChar,addr resultInt,lengthC ;将dword数组逆序并转化为byte数组
	;根据正负号进行相对的打印
	.if negativeFlag == 1
		invoke crt_printf, addr outputMsg2,addr negativeImg, addr resultChar
	.else 
		invoke crt_printf, addr outputMsg, addr resultChar
	.endif

	ret
main endp
end main

你可能感兴趣的:(计算机体系结构,系统架构,硬件架构)