基础算法,高精度加法详解

在之前的程序中,用到加法,我们可以定义这样一个函数

int add(int x, int y){
	return x + y;
}

这是最简单的一种加法的定义,也算是我们最为常用的.
假如现在需求变更,需要求百位数字之间的加法运算结果,那么该如何去做呢?
在我们之前所学习过的类型中,unsigned long long类型是目前C语言中精度最高的数据类型,而它所能表示的最大数据也才到2 ^ 64 - 1,这样直接去利用加发定义,必然会数据溢出,超出范围,在这种情况和条件之下,为了满足我们的百位数字加减法,我们只能用到数组.方然,为了保证数字的连贯,我们用字符数组来表示,之后再利用相应的ASCII码值进行转换即可.
既然有了思路,我们先试着写一写.

//先定义两个字符数组,初始化为0
char a[100] = { 0 };
char b[100] = { 0 };
//定义字符数组sum来储存结果
char sum[101] = { 0 };
scanf("%s %s",&a, &b);	//注意这个时候我们输入的数字就用字符串来表示了.

想想我们列竖式计算加法时的思想是怎样的.将两个数字对应的个,十,百…位都对齐进行相对应的加法计算,满10就进1,不满则不进.
既然我们已经用字符数组的形式将数字表示了出来,那我们就可以利用取下标将对应的数字进行相加.但我们要注意一个问题,我们通常的列竖式加法运算是从末尾开始逐次相加,对应到我们的字符数组中,我们就要得到两个字符数组中较长的作为边界条件,来写这个for循环.
假设两个字符数组中较长的位maxlenth

maxlenth = lenth_a > lenth_b ? lenth_a : lenth_b;

利用三目运算符我们很容易找到这个边界条件.
int i ;
for (i = maxlenth - 1; i >= 0; --i){

} 

先不考虑结果,如果我们写成如上这样的for语句可以吗?
答案是否定的,因为写成这样,长度较小的字符数组必然会出现越界的情况,导致这样的加法程序不合理.
既然反着行不通,那么我们就把这个数组进行处理,将其反转,这样我们正着加过去,即使是长度较小的字符数组也不会出现越界的情况,就会如同我们列竖式去计算一样,默认去补0,也就是说这种处理方式就避免了在进行计算的过程中出现越界的情况.

int DealArray(char* a){
	int lenth = strlen(a);
	int i, j;
	int tmp;
	for (i = 0, j = lenth - 1; i <= j; ++i, --j){
			tmp = a[i] - '0';
			a[i] = a[j] - '0';
			a[j] = tmp;
	}
	return lenth;
}

如上所示的处理,就是将字符数组反转的一个过程,与此同时,我们将字符数组的字符数字转化成了其对应的十进制数字,也就位后面加法的计算做好了铺垫.(在ASCII码表中,‘0’ - '9’对应的ASCII码值是48 - 57,减去’0’也就是进行对应的数字转化).在进行数组处理的同时,我们返回其字符数组对应的长度,方便结果的输出,也就是说,为了将我们的结果显示出来,我们再用一个函数输出结果

void OutputNumber(char* a, int lenth){
	int i;
	for (i = lenth - 1; i >= 0; --i){
		printf("%c",arr[i] + '0');	
		//由于前面对于数组的出来使其反转并转化了对应的数字,所以我们需要逆序打印,并将其再度转化位字符数字的形式
	}
}

接下来就是我们最重要的算法了,也就是这个高精度加法的核心部分,先看代码

int Add(char* a, char* b, int maxlen, char* sum){
	int tmp;
	int i;
	for(i = 0; i < maxlen; ++i){
		tmp = a[i] + b[i] + sum[i];	//对应位置数字进行相加,这里的sum[i]表示是否进位
		sum[i] = tmp % 10;	//这里的sum[i]储存的是每次相加的结果
		sum[i + 1] = tmp / 10;	//这里的sum[i + 1]决定下次相加是否要多加1也就是是否进位
	}
	return maxlen + sum[i];
}

maxlen就是字符数组a和b中较长的作为边界条件.
sum[]就是我们用来储存加法运算结果的字符数组.
sum[i + 1]的作用就是进位.
为了后面方便结果输出,所以我们将长度作为返回值,当跳出循环时,i的值为maxlen,也就说此时sum[]中保存的字符数组长度也就是maxlen,注意这并不是说结果的长度就是maxlen,因为在跳出循环之前(i的值为maxlen - 1)的sum[i + 1],也就是sum[maxlen]还在执行,就是在进位,进位之后,i自增1,变为maxlen,跳出循环,返回值为maxlen + sum[i],也就是maxlen + sum[maxlen],如果没有进位就是+0,有就是加1,这才是最终sum[]长度的值.
完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int DealArray(char* a){
	int i, j;
	int tmp;
	int lenth = strlen(a);
	for (i = 0, j = lenth - 1; i <= j; ++i, --j){
		tmp = a[i];
		a[i] = a[j] - '0';
		a[j] = tmp - '0';
	}
	return lenth;
}
void OutputNumber(char* a,int lenth){
	int i;
	for (i = lenth - 1; i >= 0; --i){
		printf("%c",a[i] + '0');
	}
}
int Add(char* a, char* b, int maxlen, char* sum){
	int i;
	int tmp;
	for (i = 0; i < maxlen; ++i){
		tmp = a[i] + b[i] + sum[i];
		sum[i] = tmp % 10;
		sum[i + 1] = tmp / 10;
	}
	return maxlen + sum[i];
}
int main(){
	int lenth_a, lenth_b;
	int lenth_sum;
	char a[100] = { 0 };
	char b[100] = { 0 };
	char sum[101] = { 0 };
	scanf("%s %s",&a,&b);
	lenth_a = DealArray(a);
	lenth_b = DealArray(b);
	OutputNumber(a,lenth_a);
	printf("\n");
	OutputNumber(b,lenth_b);
	printf("\n");
	lenth_sum = Add(a, b, lenth_a > lenth_b ? lenth_a : lenth_b, sum);
	OutputNumber(sum, lenth_sum);
	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44781107/article/details/89842726