大数加法
思路:
从后往前算(即由低位向高位运算),计算的结果依次添加到结果中去,最后将结果字符串反转。
输入的时候两个数都是以字符串的形式输入的,测出每个字符串的长度(也就是该数的位数),因为字符串不能直接进行运算,所以测出长度之后将字符串形的数反向转化为整形数组的形式(例如输入两个数为86369和22123,转化为整形数组形式储存为96368和32122,让低位在前,是运算更方便),然后进行运算。
要用到两个字符型数组来先保存输入的数,再定义3个整形数组,要将这三个数组中的元素开始的时候全部清零,其中两个来储存将字符型数转化过的整型数,再有一个就是来保存运算结果的数组。
最后逆序输出结果108492
核心代码
for(i=lena-1;i>=0;i--)
c[lena-1-i]=a[i]-'0';
for(i=lenb-1;i>=0;i--)
d[lenb-1-i]=b[i]-'0';
k=0;
for(i=0;i<lenb||i<lena;i++)
{
h=c[i]+d[i]+k;
f[i]=h%10;
k=h/10;//进位
}
if(k)
f[i++]=k;
例题:HDU - 1002
#include<stdio.h>
#include<string.h>
char a[10000],b[10000];
char c[1000001];
int main()
{
int n=0,i=0;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%s %s",a,b);
printf("Case %d:\n%s + %s = ",i,a,b);
int len1,len2,j=0,k=0;
len1=strlen(a)-1; len2=strlen(b)-1;
for(j=0;len1>=0||len2>=0;j++,len1--,len2--)
{
if(len1>=0&&len2>=0)
c[j]=a[len1]+b[len2]-'0'+k;
if(len1>=0&&len2<0)
c[j]=a[len1]+k;
if(len1<0&&len2>=0)
c[j]=b[len2]+k;
k=0;
if(c[j]>'9')
{
c[j]=c[j]-10;
k=1;
}
}
if(k)
printf("1");
while(j--)
{
printf("%c",c[j]);
}
if(i<n)
printf("\n\n");
else
printf("\n");
}
return 0;
}
高精度加法
思路:
找到小数点把整数部分和小数部分分开
先计算小数部分再计算整数部分(防止小数部分进位)
使用数组存储数,再逆序模拟加法
计算过程和大数加法一样(小数部分位数小的注意补0)
输出的时候注意判断小数点是否存在
#include<stdio.h>
#include<string.h>
char s1[500],s2[500];
int a[500],b[500],c[500],d[500];
int main(){
int i,j,k,len1,len2,s,t,kk1,kk2;
while(scanf("%s %s",s1,s2)!=EOF)
{
//别忘了初始化
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
len1=strlen(s1);
len2=strlen(s2);
s=len1;
t=len2;
//找到小数点位置
for(i=0;i<len1;i++)
if(s1[i]=='.')
{
s=i;
break;
}
for(i=0;i<len2;i++)
if(s2[i]=='.')
{
t=i;break;
}
//小数部分 //先算小数部分防止有进位
k=1;
if(s+1<len1)
for(j=s+1;j<=len1-1;j++)
c[k++]=s1[j]-'0';
k=1;
if(t+1<len2)
for(j=t+1;j<=len2-1;j++)
d[k++]=s2[j]-'0';
kk1=len1-s;
if(len2-t>kk1)
kk1=len2-t;
for(i=kk1;i>=1;i--)
{
c[i]+=d[i];
if(c[i]>=10)
{
c[i]-=10;
c[i-1]++;
}
}
//整数部分
k=0;
for(j=s-1;j>=0;j--)
a[k++]=s1[j]-'0';
k=0;
for(j=t-1;j>=0;j--)
b[k++]=s2[j]-'0';
kk2=s-1;
if(t-1>kk2)
kk2=t-1;
a[0]+=c[0];//小数进位
for(i=0;i<=kk2;i++){
a[i]+=b[i];
if(a[i]>=10)
{
a[i]-=10;
a[i+1]++;
}
}
//输出整数部分
if(a[kk2+1])
printf("%d",a[kk2+1]);
for(i=kk2;i>=0;i--)
printf("%d",a[i]);
//输出小数部分
for(i=kk1;i>=1;i--)
if(c[i])
break;
if(i)//判断是否有小数
{
printf(".");
for(j=1;j<=i;j++)
printf("%d",c[j]);
}
printf("\n");
}
return 0;
}
大数减法
基本思路和加法类似,进位变为借位,在运算之前需要判断正负,将大的数放在被减数上,因此,在需要的时候将被减数和减数调换一下位置。
首先,要判断减数和被减数哪一个位数长,减数位数长是正常减;被减数位数长,则被减数减减数,最后还要加上负号;两数位数长度相等时,最好比较一下哪一个数字大,否则负号处理会很繁琐,用大的减去小的,最后加上负号;
其次,处理每一项时要,如果前一位相减有借位,就先减去上一位的借位,无则不减;再去判断是否能够减开被减数,如果减不开,就要借位后再去减,同时置借位为1,否则置借位为0。
结果可能会出现前面是一堆0的情况,要处理好,如当减数为112,而被减数为111时,会出现001 ,这时,需要将前面的0删除。
输出时将0删除,并加上负号,即-9896
#include<stdio.h>
#include<string.h>
int x[11000]={
0},y[11000]={
0},z[11050]={
0};//将数组元素全部初始化为0
void sub(int x[],int y[],int len)
{
int i,j;
for(i=0;i<len;i++)
{
if(x[i]>=y[i])//如果x[i]>=y[i],不用向前一位借1,可直接减
z[i]=x[i]-y[i];
else //如果x[i]<y[i],向前一位借1,同时前一位应减1
{
z[i]=x[i]+10-y[i];
x[i+1]=x[i+1]-1;
}
}
for(i=len-1;i>0;i--)//删除前缀0
{
if(z[i])
break;
}
for(;i>=0;i--) //倒序输出数组
printf("%d",z[i]);
// printf("\n");
}
int main()
{
char a[100],b[100];//通过字符串对大数进行输入并储存
int len1,len2;
while(~scanf("%s %s",a,b))
{
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
memset(z,0,sizeof(z));
int i,j=0,k=0;
len1=strlen(a);
len2=strlen(b);
for(i=len1-1,j=0;i>=0;i--)//将两个字符串中的字符转化为数字,并倒序储存到数组中,即字符串为123456,则数组为654321
x[j++]=a[i]-'0';
for(i=len2-1,k=0;i>=0;i--)
y[k++]=b[i]-'0';
if(len1>len2) //若减数长度 > 被减数,正常减
sub(x,y,len1);
else
if(len1<len2) //若减数长度 < 被减数,被减数 减 减数
{
printf("-");
sub(y,x,len2);
}
else //若减数长度 == 被减数,判断两个数的大小
{
k=0;
for(i=len1-1;i>=0;i--)//判断每一位两个数的大小
{
if(x[i]==y[i])
{
k++;
continue;
}
if(x[i]>y[i])//即减数大
{
sub(x,y,len1);
break;
}
if(x[i]<y[i])//即被减数大
{
printf("-");
sub(y,x,len1);
break;
}
}
if(k==len1)
{
printf("0");
}
}
}
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char a[10001];
char b[10001];
int a1[10001];
int b1[10001];
int c[10001];
int main()
{
scanf("%s",&a);
scanf("%s",&b);
int la=strlen(a);
int lb=strlen(b);
if(lb>la||(la==lb&&strcmp(a,b)<0))
{
swap(a,b);
printf("-");
}
la=strlen(a);
lb=strlen(b);
for(int i=0;i<la;i++)
{
a1[i]=a[la-i-1]-'0';
}
for(int i=0;i<lb;i++)
{
b1[i]=b[lb-i-1]-'0';
}
int i=0;//当前位数
while(i<la)
{
if(a1[i]-b1[i]<0)
{
a1[i]=a1[i]+10;
a1[i+1]--;
}
c[i]=a1[i]-b1[i];
i++;
}
while(1)
{
if(c[i]==0&&i>=1)
i--;
else break;
}
for(int j=i;j>=0;j--)
printf("%d",c[j]);
return 0;
}
大数乘法
第一个数据的第i位与第二个数据的第j位相乘存放在结果的第[i+j]个元素中,因为结果的每一位不是顺序得出,所以不方便用字符串存储,转而用数组存储。另一方面,第一步不考虑进位的问题,先将相乘的结果保存在数组中,然后对数组中的元素需要进位的按进位规则处理。
即一个数的第i 位和另一个数的第j 位相乘所得的数,一定是要累加到结果的第i+j 位上。这里i, j 都是从右往左,从0 开始数。
c[i+j] = a[i]*b[j];
注意进位时要处理,当前的值加上进位的值再看本位数字是否又有进位;前导清零
例:22123*83
逆序输出得到结果1836209
#include<stdio.h>
#include<math.h>
#include<string.h>
int main()
{
char a[1000],b[1000],i,j,k;
int c[10000],x[1000],y[1000];
memset(c,0,sizeof(c));
scanf("%s %s",a,b);
int len1=strlen(a);//a位数
int len2=strlen(b);//b位数
for(i=len1-1,j=0;i>=0;i--)//将字符串中各个元素倒序储存在数组中
{
x[j++]=a[i]-'0';
}
for(i=len2-1,k=0;i>=0;i--)
{
y[k++]=b[i]-'0';
}
for(i=0;i<len2;i++)
{
k=i;
for(j=0;j<len1;j++)
{
if(c[k]!=0)
{
c[k]+=y[i]*x[j];
}
else
c[k]=y[i]*x[j];
k++;
}
}
for(i=0;i<=k;i++)
{
if(c[i]>=10)
{
c[i+1]+=c[i]/10;
c[i]=c[i]%10;
}
}
for(i=k;i>=0;i--)
{
if(c[i])
break;
}
for(;i>=0;i--)
printf("%d",c[i]);
printf("\n");
return 0;
}
大数乘法(幂运算)
#include<stdio.h>
#include<math.h>
#include<string.h>
int main()
{
int n,m,i,j;
int f[3000];
while(scanf("%d %d",&n,&m),n||m)
{
memset(f,0,sizeof(f));
int k=m+2;
if(n==1)
{
printf("1\n");
continue;
}
if(n==10)
{
printf("1");
for(int i=1;i<=m;i++)
printf("0");
printf("\n");
continue;
}
int sum;
f[0]=1;
for(i=1;i<=m;i++)
{
int c=0;
for(j=0;j<k;j++)
{
sum=f[j]*n+c;
f[j]=sum%10;
c=sum/10;
}
}
for(j=k-1;j>=0;j--)
if(f[j])
break;
for(i=j;i>=0;i--)
{
printf("%d",f[i]);
}
printf("\n");
}
return 0;
}
大数乘法(高精度幂运算)
POJ - 1001
浮点数求高精度幂 OpenJ_Bailian - 2951
#include<stdio.h>
#include<string.h>
char c[1000];
void kk(char a[],char b[])
{
int i,j,num,k1;
int k[1000];
memset(k,0,sizeof(k));
int len=strlen(a);
int len1=strlen(b);
for(i=len-1; i>=0; i--)
{
for(j=len1-1,k1=len-1-i;j>=0; j--,k1++)
{
num=(a[i]-'0')*(b[j]-'0')+k[k1];
k[k1]=num%10;
k[k1+1]+=num/10;
}
}
for(i=k1;i>=0; i--)
{
if(k[i])
break;
}
for(j=0;i>=0; i--)
c[j++]=k[i]+'0';
}
int main()
{
char a[51];
memset(a,0,sizeof(a));
int n,i,k,num,flag;
while(scanf("%s%d",a,&n)!=EOF)
{
int kkn=n;
int k1=0;
int numk=0;
int len=strlen(a);
memset(c,0,sizeof(c));
int sum=0;
int fort=0;
for(i=0,num=0; i<len; i++)
{
if(a[i]=='.')
{
k=len-1-i;
for(;i<len;i++)
a[i]=a[i+1];
num=k*n;
numk=num;
break;
}
else
{
fort=1;
k1=i;
}
}
if(fort)
{
for(;a[k1]=='0';k1--)
sum++;
}
for(i=0; i<len; i++)
if(a[i]!='0')
break;
for(k=0; i<=len; i++)
a[k++]=a[i];
strcpy(c,a);
n--;
while(n--)
{
kk(c,a);
}
int len1=strlen(c);
if(num<=len1)
{
for(i=len1-1; i>=0; i--)
if(c[i]!='0'&&c[i]!='.')
{
flag=i;
break;
}
for(i=0; i<len1; i++)
{
printf("%c",c[i]);
if(i==len1-num-1)
{
if(i==flag)
break;
printf(".");fort=0;
}
if(flag==i)
break;
}
num++;
}
else
{
for(i=len1-1; i>=0; i--)
if(c[i]!='0')
{
c[i+1]='\0';
break;
}
printf(".");fort=0;
for(i=len1; i<num; i++)
{
printf("0");
}
printf("%s",c);
}
if(fort)
{
int sum1=sum*kkn;
while(sum1--)
printf("0");
}
printf("\n");
}
return 0;
}
大数除法
基本思想是反复做除法,看从被除数里面最多能减去多少个除数,商就是多少。逐个减显然太慢,要判断一次最多能减少多少个整数(除数)的10的n次方。
以7546除以23为例:
先用7546减去23的100倍,即减去2300,可以减3次,余下646,此时商就是300 (300=100*3);
然后646减去23的10倍,即减去230,可以减2次,余下186,此时商就是320 (320=300+10*2);
然后186减去23,可以减8次,余下2,此时商就是328 (328=320+1*8);
因为2除以23的结果小于1,而我们又不用计算小数点位,所以不必再继续算下去了。
#include<stdio.h>
#include<string.h>
char a[100],b[100];//用两个字符串用来输入两个大数
int x[100],y[100],z[100],m[100];//被除数 除数 商 余数
int digit;//大数的位数
void sub(int x[],int y[],int len1,int len2)//大数减法
{
int i;
for(i=0;i<len1;i++)
{
if(x[i]<y[i])
{
x[i]=x[i]+10-y[i];
x[i+1]--;
}
else
x[i]=x[i]-y[i];
}
for(i=len1-1;i>=0;i--)//判断减法结束之后,被除数的位数
{
if(x[i])
{
digit=i+1;
break;
}
}
}
int judge(int x[],int y[],int len1,int len2)
{
int i;
if(len1<len2)
return -1;
if(len1==len2)//若两个数位数相等
{
for(i=len1-1;i>=0;i--)
{
if(x[i]==y[i])//对应位的数相等
continue;
if(x[i]>y[i])//被除数 大于 除数,返回值为1
return 1;
if(x[i]<y[i])//被除数 小于 除数,返回值为-1
return -1;
}
return 0;//被除数 等于 除数,返回值为0
}
}
int main()
{
int i,j=0,k=0,temp;
int len1,len2,len;//len两个大数位数的差值
while(~scanf("%s %s",a,b))
{
len1=strlen(a);//被除数位数
len2=strlen(b);//除数位数
for(i=len1-1,j=0;i>=0;i--)//将字符串中各个元素倒序储存在数组中
x[j++]=a[i]-'0';
for(i=len2-1,k=0;i>=0;i--)
y[k++]=b[i]-'0';
if(len1<len2)//当被除数位数 小于 除数位数时
{
printf("商是:0\n");
printf("余数是:");
puts(a);
}
else //当被除数位数 大于或者 除数位数时
{
len=len1-len2;//两个大数位数的差值
for(i=len1-1;i>=0;i--)//将除数后补零,使得两个大数位数相同。被除数:4541543329 除数:98745,加零后:9874500000
{
if(i>=len)
y[i]=y[i-len];
else
y[i]=0;
}
len2=len1;//将两个大数数位相同
digit=len1; //将原被除数位数赋值给digit
for(j=0;j<=len;j++)
{
z[len-j]=0;
while(((temp=judge(x,y,len1,len2))>=0)&&digit>=k)//判断两个数之间的关系以及位数与除数原位数的关系
{
sub(x,y,len1,len2); //大数减法函数
z[len-j]++;//储存商的每一位
len1=digit;//重新修改被除数的长度
if(len1<len2&&y[len2-1]==0)
len2=len1;//将len1长度赋给len2;
}
if(temp<0)//若被除数 小于 除数,除数减小一位。例如:被除数:4541543329 除数:(原)98745,(加零后)9874500000,后退一位后:0987450000
{
for(i=1;i<len2;i++)
y[i-1]=y[i];
y[i-1]=0;
if(len1<len2)
len2--;
}
}
printf("商是:");
for(i=len;i>0;i--)//去掉前缀0
{
if(z[i])
break;
}
for(;i>=0;i--)
printf("%d",z[i]);
printf("\n");
printf("余数是:");
for(i=len1;i>0;i--)
{
if(x[i])
break;
}
for(;i>=0;i--)
printf("%d",x[i]);
printf("\n");
}
}
return 0;
}
#include <stdio.h>
#include <string.h>
int dividend[100],divisor[100],quotient[100],len1,len2;
char line1[101],line2[101];
/***************************************************
长度为lenn1的大整数p1减去长度为lenn2的大整数p2
减得结果放在p1里返回值代表结果的长度,不够减返回-1,正好剪完返回0
****************************************************/
int substract(int* p1,int* p2,int lenn1,int lenn2)
{
int i;
if(lenn1 < lenn2) return -1;
if(lenn1 == lenn2)
for(i = lenn1-1;i>=0;i--)
{
if(p1[i] > p2[i])
break;
else if(p1[i] < p2[i]) return -1;
else continue;
}
for(i = 0;i < lenn1;i++)
{
p1[i] -= p2[i];
if(p1[i] < 0)
{
p1[i] += 10;
p1[i+1]--;
}
}
for(i = lenn1-1;i >= 0;i--)
if(p1[i]) break;
return i+1;
}
int main()
{
int i,j;
gets(line1);
gets(line2);
len1 = strlen(line1);
len2 = strlen(line2);
for(i = len1-1,j = 0;i >= 0;i--,j++)
dividend[j] = line1[i] - '0';
for(i = len2-1,j = 0;i >= 0;i--,j++)
divisor[j] = line2[i] - '0';
len1 = substract(dividend,divisor,len1,len2);
if(len1 == -1)
{
printf("0");
return 0;
}
if(len1 == 0)
{
printf("1");
return 0;
}
quotient[0]++;
int times = len1 - len2;
for(i = len1-1;i >= 0;i--)
{
if(i >= times)
divisor[i] = divisor[i - times];
else
divisor[i] = 0;
}
len2 = len1;
for(j = 0;j <= times;j++)
{
int tmp;
while((tmp = substract(dividend,divisor+j,len1,len2-j)) >= 0)
{
len1 = tmp;
quotient[times-j]++;
}
}
for(i = 0;i < 99;i++)
if(quotient[i] >= 10)
{
quotient[i+1] += quotient[i]/10;
quotient[i] %= 10;
}
i = 99;
while(!quotient[i] && i >=0) i--;
if(i == -1)
printf("0");
else
while(i >= 0)
printf("%d",quotient[i--]);
return 0;
}