高精度运算(加法,减法,乘法)

写在最前面:

最近在洛谷上写题的时候,看到一个算1000!的题目,但是C语言和C++算到13的阶乘,就存放不下了,所以就根据答案,写了一个算大数阶乘的程序,与此同时,受到启发,就又写了写大数的加法减法和乘法,对于除法,我现在唯一想到的办法是进行多次减法,所以等以后有了好的想法再补上!

什么是高精度?

对于C /C++,它们的数据类型有限,int最大为2^31 -1, 大约二十亿,long long最多是2^63-1,十进制也就二十位左右。
但是,如果一个数超过了20位,甚至一百位,两百位,那么这些基本的数据类型就不足以进行计算了。因此,我们就需要用到高精度的运算,说白了就是用数组来存放大数,然后模拟列竖式的过程就行。接下来依次说明

基本原则

1.按正常字符串的方式读入数字,读入之后都减去’0’变成数字
2.运算和进位分开做,先按位进行运算,之后统一处理进位,这样不会搞混
3.运算的时候从最后一位开始,把结果写进另一个数组里,这里指的是正序写入另一个数组,这就需要在输出结果时逆序输出了
No Picture You Say a …
在这里插入图片描述

1.加法

加法比较容易,直接按位相加就行。加法函数里,为了避免分类讨论两个加数位数的问题,所以把两个加数都加到了同一个数组里。这是最重要的部分
这里主要是来写一下整体的代码,后面乘法和减法里相似的部分就不再说明了

#include <stdio.h>
#include <string.h>

void toNum(char s[]);//转换位数字
int add(char a[], int len1, char b[], int len2, int result[]);//按位相加
int carry(int result[], int len);//进位

int main()
{
	char a[400];
	char b[400];
	int result[410]={0};
	
	//buffer
	
	scanf("%s", a);
	scanf("%s", b);
	int len1 = strlen(a);
	int len2 = strlen(b);
	
	toNum(a);
	toNum(b);
	
	int len3 = add(a,len1, b, len2, result);
	int len4 = carry(result, len3);
	
	int i=0;
	
	for (i=len4-1; i>=0; i--)
	{
		printf("%d", result[i]);
	}
	printf("\n");
	return 0;
}

void toNum(char s[])
{
	int i;
	int len = strlen(s);
	
	
	for (i=0; i<len; i++)
	{
		s[i] -= '0';
	}
}

int add(char a[], int len1, char b[], int len2, int result[])
{
	int i=len1-1, j=len2-1, k1=0, k2=0;
	while (i>=0)
	{
		result[k1] += a[i];
		i--;
		k1 ++;
	}
	//先把第一个数加到result数组里
	while (j>=0)
	{
		result[k2] += b[j];
		j--;
		k2 ++;
	}
	//再把第二个数也加到result数组里
	return (k1>k2?k1:k2);//返回大的位数,实际操作了多少位
}
int carry(int result[], int len)
{
	int i=0;
	
	for (i=0; i<len; i++)
	{
		result[i+1] += result[i]/10;//一定要先进位再取余
		result[i] %= 10;
	}
	
	if (result[len]==0)//如果最高位是0,就说明没进位
		return len;
	else
		return len+1;
}

接下来的代码都是由三个部分构成,转换为数字(toNum),运算(add,sub,multi)
,进位(carry)等,主要说明的是运算函数

2.减法

减法和加法大同小异,首先是按位相减,然后是进位变成了借位,借位就是如果这一位小于0,就让前一位-1,这一位加10
No Picture You Say a…
在这里插入图片描述大致思路就是这样,下面是代码

int sub(char a[], int len1, char b[], int len2, int result[])
{
	int i=len1-1, j=len2-1, k1=0, k2=0;
	while (i>=0)
	{
		result[k1] += a[i];//也不是非得用+=,直接赋值就行
		i--;
		k1 ++;
	}
	//把第一个数赋值给result,这样才能减
	while (j>=0)
	{
		result[k2] -= b[j];
		j--;
		k2 ++;
	}
	return k1;
}

int carry(int result[], int len)//这是借位的处理
{
	int i=0;

	for (i=0; i<len; i++)
	{
		if (result[i]<0)//小于0时才能说明借位了
		{				//然后加10减1就行
			result[i] += 10; 
			result[i+1]--;
		}
	}

	while(result[len-1]==0&&len>1)//把前导0都去了,才能得到位数
	{
		len--;
	}
	return len;
}

这里需要说明的就是:减法函数里默认了第一个数是大于第二个数的,这样也就更简单了,实际上,为了达到这个效果,前面可以再加一个缓冲区,专门用来调换顺序,控制正负号。减法就说到这里

3.乘法

乘法最重要的是乘法函数的设计,与加减法不同,乘法需要一个数每位都与另一个数相乘再相加,加的时候要对齐,比如第二位进行相乘,就从第二位开始往后加。其实,这样也还没解释清楚,先从低精度乘高精度说吧
低精度就是说int能表示的数,比如9,100这些,相乘的时候让这些数和高精度的每一位都相乘,按位记录下来,乘完之后再考虑进位,
No Picture You Say a…
在这里插入图片描述这和实际的程序还是有点出入的,主要是顺序不一样,这个图为了说明实际的原理也就没考虑那么多的细节了,应该很清楚了吧
接下来解释高精度乘以高精度
这是对前面的一个扩展,需要把每一位都当作低精度来和另一个数相乘,然后再把每一位的积相加,这里关键在于相加时候的对齐问题,考虑到每次前进一位就相当于乘以了10,所以在相加的时候也要先乘10,反映到数组里就是往前走一位对齐。也就是说,这是第几位,就要把积从第几位开始对齐相加
No Picture You Say a …
在这里插入图片描述后面就是求和,化的图比较丑,就不再画了(逃)

终于到了代码时间,上代码

int multi(char a[], int len1, char b[], int len2, int result[])
{
	if (a[0]==0||b[0]==0)
	{
		return 1;
	}//如果输入两个0的话,就不需要乘了
	
	int i, j, k=0;
	
	for (i=len1-1; i>=0; i--)
	{
		k = len1-1-i;//这是负责对齐的
		for (j=len2-1; j>=0; j--)
		{
			result[k] += a[i]*b[j];
			k++;
		}
	}
	
	return k;
}

int carry(int num[], int len)
{
	int i;

	for (i=0; i<len; i++)
	{
		num[i+1] += num[i]/10;
		num[i] %= 10;
	}
	
	while (num[i])
	{
		num[i+1] += num[i]/10;
		num[i] %= 10;
		i++;
	}//这是为了把每个单元都变成只有一个数字,方便后续使用
	
	return i;
}

至于除法,还没有想到高级的模拟方法,等以后想到了再补充上。

最后,这些其实都不算完美,比如没有对正负号进行判断,只是用于整数等,这些不足都还有待改进,总之,不断地学习新知识,不断总结积累,才能有更完善的思路,更优质的解题过程。这次的总结,就先到这里吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章