基于基础的汇编大数乘法,对于特例以及异常情况增加了判断。
1.对于输入的乘数中含非数字的非法数,提示“输入错误”;
2.对于乘数为0,输出结果为0;
3.对于000等连续零,理解为0;
4.对于012理解为12,即忽略乘数的前导0.
.386
.model flat, stdcall
option casemap : none
includelib msvcrt.lib
includelib kernel32.lib
include msvcrt.inc
endl equ <0DH,0AH>
.data
;字符数组,按位存取输入乘数,初始化为0
charArray_A byte 500 dup(0)
charArray_B byte 500 dup(0)
;字符数组,用于存储结果的字符数据,初始化为0
charResult byte 1000 dup(0)
;整数数组定义和初始化
intArray_A dword 500 dup(0)
intArray_B dword 500 dup(0)
intResult dword 1000 dup(0)
;输入格式
inputMsg1 byte "请输入乘数A: ",0
inputMsg2 byte "请输入乘数B: ",0
szFmt_s byte "%s", 0
szFmt_d byte "%d", 0
;输出格式
outputMsg byte "计算结果是:%s",endl,0;正数结果
outputMsg2 byte "计算结果是:-%s",endl,0;负数结果
outputMsg0 byte "计算结果是:0",endl,0;零结果
outputMsg1 byte "输入错误",endl,0;乘数输入格式不对,含有非数字(首位为负号不算错误)
;数组长度初始化为0
lengthA dword 0
lengthB dword 0
lengthC dword 0
;进制,默认为10
radix dword 10
;符号标志位,初始为0,表示正数,1表示负数
negativeFlag byte 0
.code
; 检查乘数是否合法,只包含数字(首位为负号不算错误),并记录乘数中0的个数(若乘数为全0,结果为0)
checkValidMultiplier proc far C uses esi ebx,addChar:ptr byte
mov esi, addChar ; 乘数数组的地址
xor ebx, ebx ; ebx 用于循环计数和存储字符
mov al, [esi] ; 读取第一个字符
.if al == 2DH ; 如果第一个字符是负号
inc ebx ; 跳过负号
.endif
checkLoop:
mov al, [esi + ebx] ; 读取当前字符
.if al == 0 ; 如果到达字符串末尾,说明检查通过
ret
.endif
cmp al, 30H ; 检查当前字符是否是数字字符的ASCII码
jl invalidMultiplier ; 如果小于 '0',说明不是数字,跳转到错误处理
cmp al, 39H ; 检查当前字符是否大于 '9'
jg invalidMultiplier ; 如果大于 '9',说明不是数字,跳转到错误处理
inc ebx ; 继续下一个字符
jmp checkLoop ; 继续检查下一个字符
invalidMultiplier:
; 输出错误消息
invoke crt_printf, addr outputMsg1, endl
; 退出程序
invoke crt_exit, 1
ret
checkValidMultiplier endp
;把字符转换成数字,再利用高位先入栈、低位先出栈实现倒序存储
str2int_reverse proc far C uses eax ecx esi,numChar:ptr byte, numInt:ptr dword, len:dword
mov ecx, len ;循环次数
mov esi, numChar
L1:
movzx eax, byte ptr [esi];零扩展
sub eax, 30H ;减0
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
;计算带符号的数字长度,并调用翻转函数
calcLen proc far C uses eax ebx esi,numCharX:ptr byte,numIntX:ptr dword,len:ptr dword
mov ebx,numCharX
mov esi,len
.if byte ptr[ebx] == 2DH ;2DH对应ASCII码为'-'
xor negativeFlag, 1 ;遇到一次负号,改变一次flag
inc ebx ;指针向后移动一位,跳过符号位
.endif
invoke crt_strlen, ebx ;调用函数计算字符串长度,结果存在eax中
mov dword ptr [esi], eax ;长度存到len所指int中
invoke str2int_reverse, ebx, numIntX,dword ptr [esi]
ret
calcLen endp
;计算结束把int的倒序数组,转换成char的正序数组
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
inc esi
loop L2
ret
int2str_reverse endp
;模拟手算的大数相乘算法:A的第i位与B的第j位相乘,结果存到结果的i+j位
bignum_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 intArray_A[4 * ebx]
mul intArray_B[4 * ecx] ;intArray_A[4 * ebx] * intArray_B[4 * ecx]结果放在EAX中
mov esi, ecx
add esi, ebx ;esi = ecx + ebx,下标和
add intResult[4 * esi], eax ;把两位相乘的结果加到intResult的相应位上
inc ecx
cmp ecx, lengthB
jnb OuterLoop ;下标超过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:
;进位:result[i+1]+=result[i]/10,result[i]+=result[i]%10
cmp ebx, ecx
jnb endLoop2 ;ebx >= ecx跳出求进位的循环
mov eax, intResult[4 * ebx]
xor edx, edx
div radix ;进制为10
add intResult[4 * ebx + 4], eax ;intResult[i+1] += intResult[i]/10
mov intResult[4 * ebx], edx ;intResult[i] = intResult[i] % 10
inc ebx
jmp carryCul
endLoop2:
mov ecx, lengthC ;做清0检查
clearZero:
;清零:当i位数与j位数相乘时,最终结果不一定是i+j位,从最高位依次检测结果数组中的值是否为0,为0则长度减1
cmp dword ptr intResult[4 * ecx], 0
jnz endLength ;intResult的末位不为0,高位0清除结束
cmp ecx,0;清零把结果都清完了,证明结果是0
jz finalZero
dec ecx ;每检测到一个0,实际长度减1
jmp clearZero
endLength:
inc ecx ;实际长度为最大下标加1
mov esi, offset lengthC
mov [esi], ecx ;将ecx赋给lengthC
ret
finalZero:
; 输出结果为0
invoke crt_printf, addr outputMsg0, endl
; 退出程序
invoke crt_exit, 1
ret
bignum_multiply endp
;主函数
main proc
;输入两个乘数A和B,并用字符数组存储
invoke crt_printf, addr inputMsg1
invoke crt_scanf, addr szFmt_s, addr charArray_A
invoke checkValidMultiplier,addr charArray_A;检查乘数A输入是否合法
invoke calcLen,addr charArray_A,addr intArray_A,addr lengthA;计算乘数A长度,翻转为数字数组
invoke crt_printf, addr inputMsg2
invoke crt_scanf, addr szFmt_s, addr charArray_B
invoke checkValidMultiplier,addr charArray_B;检查乘数B输入是否合法
invoke calcLen, addr charArray_B,addr intArray_B,addr lengthB;计算乘数B长度,翻转为数字数组
;从低位到高位计算大数乘法,结果送到intResult
invoke bignum_multiply
;将intResult逆序翻转为charResult
invoke int2str_reverse,addr charResult,addr intResult,lengthC
;根据正负号不同进行输出
.if negativeFlag == 1
invoke crt_printf, addr outputMsg2, addr charResult
.else
invoke crt_printf, addr outputMsg, addr charResult
.endif
ret
main endp
end main