汇编大数乘法

基于基础的汇编大数乘法,对于特例以及异常情况增加了判断。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值