寒士的文章
大数乘法的模式还是很僵硬的,就那个流程,具体的细节看个人如何优化。
实现两个十进制大整数的相乘(100位以上),输出乘法运算的结果。
Microsoft Visual Studio 2017+masm 32
.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