大整数的加法、减法、乘法,附完整代码
本人萌新一个,要是有大佬发现bug还请一定要告诉我
一:大整数存储原理
用链栈的方式,每输入一个数字就压栈,同时用一个常量step记录数字长度放在栈顶方便后续使用,以“#”结束,分别将两个操作数压入S1和S2,结果放入S栈,完美实现任意长度的数字存储
(毕竟输入时是从左到右输入,计算时是从右到左计算,输出时又是从左往右,与栈的特点相符,经过尝试,加减运算全程确实完全符合这一规律)
二:加法
从数字个位开始即从栈顶开始,每次从S1和S2各弹出一个数,用k记录是否进位(0、1),进行一位数加法运算(S1+S2+k),将(S1+S2+k)%10压入S栈,循环执行下去,若其中一个栈已空,即直接把另一个栈加上进位k压入结果S栈,直到两个栈都为空。
三:减法
方法和加法类似,进位k在0和-1间选择
只是开始时先比较两个操作数的位数,若S1<S2则交换S1和S2位置,在输出答案前加上“-”;
bug:当两个数位数相同但实际数值S1<S2时,目前会输出“error”,得不出负数结果
还请大佬指点
四:乘法
乘法的计算思路和我们正常做竖式一样
操作数存储和上述一样(我觉得可以考虑改其它数据结构可能会更简便)
计算前根据栈顶存储的操作数长度,将更长的那个放在乘数位置(竖式的上面),另一个放下面,可以减少循环次数。因为乘数会被反复使用,所以直接申请一个数组a单独存放。
每次循环,被乘数栈弹出一个数,同时用time记录是第几次循环(关系到结果后面的补0个数),与a数组从右开始进行一位数的乘法运算,k记录进位,(S2*a[i]+k)%10压入辅助栈t,(S2*a[i]+k)/10给k。每得出一个结果便加到上一个结果上(调用上面加法的方法,本人单独写了一个jiafa_2函数(实现s+t),因为发现操作数是倒过来的,在最前面加了对栈的倒转(swap函数)对于这部分可以考虑把链栈结构改为链队列),最后得到的栈s即为乘法的结果。
完整可运行代码如下:
#include<stdio.h>
#include<stdlib.h>
typedef struct Snode{
int data; //存放一位数字
struct Snode *next;
}Snode,*LinkStack;
void Push(LinkStack &S,int e) //压栈
{
LinkStack p;
p=new Snode;
p->data=e;
p->next=S;
S=p;
}
int Pop(LinkStack &S) //出栈
{
int e;
LinkStack p;
if(S==NULL)return -1;
e=S->data;
p=S;
S=S->next;
delete p;
return e;
}
void Putin(LinkStack &S1) //录入大整数,以#结束
{
char A;
int step=0; //step记录大整数位数
printf("put in num\n");
while(1)
{
step++;
scanf("%c",&A);
if(A=='#')
break;
Push(S1,A-48);
}
Push(S1,step-1); //将step压在栈顶
}
void jiafa() //大整数加法运算
{
LinkStack S1;
S1=NULL;
Putin(S1); //录入大整数S1
getchar();
LinkStack S2;
S2=NULL;
Putin(S2); //录入大整数S2
LinkStack S;
S=NULL; //栈S记录结果
int len1=Pop(S1); //len1记录S1长度
int len2=Pop(S2); //len2记录S2长度
int k=0; //记录是否进位
int c; //c作为中间量
while(S1!=NULL&&S2!=NULL) //S1、S2都不为空
{
c=Pop(S1)+Pop(S2)+k; //c为两个栈的栈顶数字之和
if(c>=10)
{
c=c-10;
k=1;
Push(S,c); //将c的个位压入栈S
}else
{
k=0;
Push(S,c);
}
}
if(S1==NULL&&S2==NULL&&k!=0) //当两个栈同时为空,如果k!=0则将k压栈
{
Push(S,k);
k=0;
}
while(S1!=NULL) //当S1不为空,将S1和k相加后压入S栈
{
c=Pop(S1)+k;
if(c>=10)
{
k=1;
c=c-10;
Push(S,c);
}
else
{
k=0;
Push(S,c);
}
}
while(S2!=NULL) //当S2不为空,将S1和k相加后压入S栈
{
c=Pop(S2)+k;
if(c>=10)
{
k=1;
c=c-10;
Push(S,c);
}
else
{
k=0;
Push(S,c);
}
}
if(k!=0)Push(S,k); //当所有栈都为空时,看k!=0时将k压栈
while(S!=NULL) //针对类似99999+1的情况
printf("%d",Pop(S));
}
void jianfa() //大整数减法运算
{
LinkStack S1; //和加法一样的录入过程,默认S1为被减数,S2为减数
S1=NULL;
Putin(S1);
getchar();
LinkStack S2;
S2=NULL;
Putin(S2);
LinkStack S;
S=NULL;
int len1=Pop(S1);
int len2=Pop(S2);
int k=0;
if(len1<len2) //当S1的位数<S2的位数,
{ // 将S1和S2交换位置,在输出结果时加-号
S=S1;
S1=S2;
S2=S;
S=NULL;
}
while(S1!=NULL&&S2!=NULL) //S1、S2都不空则循环计算
{
int a=Pop(S1); //a记录S1栈顶即减数
int b=Pop(S2); //b记录S2栈顶即被减数
if(a+k>=b) //a+k>=b即够减则直接将结果压栈
{
Push(S,a+k-b);
k=0;
}
else //不够减则将k置为-1,a加上10进行运算
{
Push(S,a+k+10-b);
k=-1;
}
}
while(S1!=NULL) //因为前面保证了S1长度大于等于S2,所以只要考虑S1是不是空
{
int a=Pop(S1);
if(a+k>=0)
{
Push(S,a+k);
k=0;
}
else //a+k<0即还要向前借位,所以当前位置为9 ,k为-1
{
k=-1;
Push(S,9);
}
}
if(k==-1) //S1、S2都为空,k仍为-1说明S1和S2位数相同但S1<S2,输出error报错
{
printf("error\n");
return;
}
if(k==0){ //k==0说明S1-S2够减
int a=0;
while((a=Pop(S))==0); //将结果栈栈顶为0的部分弹出
if(a==-1)a=0; //如果全部为0则将a置0
Push(S,a); //将弹出的不为零的项压回去
};
if(len1<len2)printf("-");
while(S!=NULL)
printf("%d",Pop(S));
return;
}
void Swap(LinkStack &S) //将栈S首位倒转
{
LinkStack T;
T=NULL;
while(S!=NULL)
{
Push(T,Pop(S));
}
S=T;
}
void jiafa_2(LinkStack &S1,LinkStack &S2) //针对乘法部分单独写的加法函数
{ //将S1和S2相加,结果存入S1
Swap(S1); //这里获得的S1,S2都是上一次加法的结果,所以顺序正好相反,将S1倒转
Swap(S2); //将S2倒转
LinkStack S;
S=NULL;
int k=0;
int c;
while(S1!=NULL&&S2!=NULL) //具体加法过程和上面相同
{
c=Pop(S1)+Pop(S2)+k;
if(c>=10)
{
c=c-10;
k=1;
Push(S,c);
}else
{
k=0;
Push(S,c);
}
}
if(S1==NULL&&S2==NULL&&k!=0)
{
Push(S,k);
k=0;
}
while(S1!=NULL)
{
c=Pop(S1)+k;
if(c>=10)
{
k=1;
c=c-10;
Push(S,c);
}
else
{
k=0;
Push(S,c);
}
}
while(S2!=NULL)
{
c=Pop(S2)+k;
if(c>=10)
{
k=1;
c=c-10;
Push(S,c);
}
else
{
k=0;
Push(S,c);
}
}
if(k!=0)Push(S,k);
S1=NULL;
S1=S; //把结果栈赋给S1
}
void chenfa() //大整数乘法运算
{
LinkStack S1; //大整数录入过程和上面相同
S1=NULL;
Putin(S1);
getchar();
LinkStack S2;
S2=NULL;
Putin(S2);
LinkStack S;
S=NULL;
int c,k=0;
int time=-1; //记录被乘数计算到第几位了
int len1=Pop(S1);
int len2=Pop(S2);
if(len1<len2) //比较S1\S2位数,将长的设为被乘数,即放在前面
{
S=S1;
S1=S2;
S2=S;
S=NULL;
c=len1;
len1=len2;
len2=c;
}
int *a=new int[len1]; //因为被乘数会被循环使用
for(int i=len1-1;i>=0;i--) //所以动态开辟数组将被乘数按位存入其中
a[i]=Pop(S1);
while(S2!=NULL) //乘数不为空即要进行一次循环
{
time++;
c=Pop(S2); //c记录乘数栈顶即最低位
if(time==0)
{
for(int i=len1-1;i>=0;i--) // 从被乘数最低位开始循环
{
Push(S,(c*a[i]+k)%10); //将结果的各位压栈,十位给k
k=(c*a[i]+k)/10;
}
if(k!=0) //被乘数已经遍历完,k!=0则把k压栈
Push(S,k);
}
else
{
LinkStack t;
t=NULL;
for(int i=0;i<time;i++) //从乘数的第二位开始,要为结果填充0
Push(t,0);
k=0;
for(int i=len1-1;i>=0;i--)
{
Push(t,(c*a[i]+k)%10);
k=(c*a[i]+k)/10;
}
if(k!=0)
Push(t,k);
jiafa_2(S,t);
}
}
while(S!=NULL)
printf("%d",Pop(S));
printf("\n");
}
int main()
{
int n;
printf("1.加法\n2.减法\n3.乘法\n0.退出\n ");
printf("put in n\n");
scanf("%d",&n);
getchar();
while(n!=0)
{
system("cls");
switch(n)
{
case 1:
{
jiafa();
break;
}
case 2:
{
jianfa();
break;
}
case 3:
{
chenfa();
break;
}
}
printf("\n1.加法\n2.减法\n3.乘法\n0.退出\n");
printf("put in n\n");
scanf("%d",&n);
getchar();
}
}