C语言笔记:高精度计算问题

原文链接: http://c.biancheng.net/c/

C语言中大数据类型的简述

我们知道,计算机内部直接使用int或者double等数据类型存储数据是有范围限制的,当运算数据较大时,计算机将会出现溢出情况,使得计算结果不够精确。例如,一个20位的十进制整数,如果用int类型变量存放,就会出现数据溢出。当运算数超出了整型、实型能表示的范围,肯定不能直接用一个数的形式来表示。在运算过程中,能表示大数的数据类型有两种:整型数组和字符串

  • 整型数组:每个元素存储1位,有多少位就需要多少个数组元素;每一位都是数的形式,可直接加减,运算时非常方便,但整型数组不能直接输入全部元素,只能一个一个输入,并且输入时,每两位数之间必须有分隔符,不符合人们输入数值的习惯,输入输出时不方便。
  • 字符串(本质上是一个字符数组):字符串的最大长度是多少,就可以表示多少位数字。用字符串表示数值能将全部位直接输入输出,但字符串中的每一个位是一个字符,必须将它转换为数值再进行运算,运算时不方便

综合整型数组和字符数组的优缺点来看,我们在接下来的问题中,用字符串读入数据,运算时转存到整型数组中进行运算,接着再转换为字符串进行输出。

事实上,高精度运算就是通过编程的方法,把简单数学的运算步骤在计算机上完美地演示一遍而已。

高精度加法

问题描述:
求两个不超过200位的非负整数的和。输入两行,每行是一个不超过200位的非负整数,没有多余的前导0;输出计算式以及相加后的结果,结果里面不能有多余的前导0。

问题分析:
用一个字符串来保存一个不超过200位大整数的数值,然后为了运算时方便,将存储大整数的字符串转移到整数数组中保存。同时因为两个数相加时要先个位对齐,然后再从低位向高位计算,而实际上在用户输入时,整数数组下标为0对应最高位,而整数数组下标最大的对应个位,所以将存放加数的字符串转移到整数数组时,要先逆置转换,即整数数组下标为0对应个位,而整数数组下标为1对应十位。。。
在这里插入图片描述

#include <stdio.h>
#include <string.h>
#define MAXLEN 210
void Invert(char *a,int *b);//将a字符逆置转换到整数数组b中,确保下标0对应个位而不是最高位 
void Output(int *p,int len);//输出整型数组元素 
int main(void)
{
 char str1[MAXLEN],str2[MAXLEN],str[MAXLEN];//存放两个加数(输入)以及和(输出)的字符串
 int a[MAXLEN],b[MAXLEN],c[MAXLEN];//存放加数以及和的整型数组(中间处理) 
 printf("输入两个加数:\n");
 scanf("%s %s",str1,str2);
 //整型数组a,b,c的元素全部清零,memset函数一般用于在对定义的字符串进行初始化为"\0",对较大的结构体或数组进行清0操作的一种最快方法 
 memset(a,0,sizeof(a));
 memset(b,0,sizeof(b)); 
 memset(c,0,sizeof(c));
 //将两个加数字字符串按位逆置存放到整型数组中,下标0对应个位
 Invert(str1,a);
 Invert(str2,b);
 printf("******************运算过程(列竖式做加法)******************\n");
 Output(a,strlen(str1));
 printf("\n");
 Output(b,strlen(str2));
 int len=strlen(str1)>=strlen(str2) ? strlen(str1):strlen(str2);//求加数较长的位数
 for(int i=0;i<len;i++)//从第一位到最高位逐位相加运算 
 {
  c[i]+=a[i]+b[i];
  c[i+1]=c[i]/10;//c[i]能除多少个10就表示进多少个位,i位的进位数存放到c[i+1]上
  c[i]%=10; //c[i]进完位后的数则是(a[i]+b[i])求模10,为余下的数 
 } 
 printf("\n");
 Output(c,len);
 printf("\n******************运算过程******************");
 while(len>=0&&c[len]==0)//和的处理,去掉前导0,并把结果复制到串中
 {
  len--;
 } 
 memset(str,0,sizeof(str));//0<=>'\0'字符串结束符 
 int i=0;
 for(int j=len;j>=0;j--)
 {
  str[i++]=c[j]+'0';//整型数字转换为字符型数字 
 } 
  if(strlen(str)==0)
 {
  str[0]='0';//结果为0的情况 
 }
 printf("\n");
 printf("运算结果:%s + %s = %s\n",str1,str2,str);
 return 0;
} 
void Invert(char *a,int *b)
{
 int len=strlen(a),j=0;
 for(int i=len-1;i>=0;i--)
 {
  b[j++]=a[i]-'0';
 }
}
void Output(int *p,int len)
{
 for(int i=0;i<len;i++)
 {
  printf("%d",p[i]);
 }
}

运算输出:
在这里插入图片描述
代码释疑:
整数数组怎么具体运算呢?即模拟小学生列竖式做加法的方法,个位对齐,从个位开始向高位逐位对应位相加,和大于或等于10则进位。在下面的源代码中,用int a[210]保存第一个加数,int b[210]保存第二个加数,然后逐位相加,两数相加的结果存储在int c[210]中,其中下面代码段要处理的就是进位情况

 for(int i=0;i<len;i++)//从第一位到最高位逐位相加运算 
 {
  c[i]+=a[i]+b[i];
  c[i+1]=c[i]/10;//c[i]能除多少个10就表示进多少个位,i位的进位数存放到c[i+1]上
  c[i]%=10; //c[i]进完位后的数则是(a[i]+b[i])求模10,为余下的数 
 } 

高精度乘法

问题描述:
求两个不超过1000位的大整数的乘积。输入两行,每行是一个不超过1000位的整数,没有多余的前导0;输出算式以及相乘后的结果。结果里不能有多余的前导0。

问题分析:
此处乘法要考虑正负号,数据结构不变,要考虑的只是将算法改变为乘法运算的模拟过程:

猜你喜欢

转载自blog.csdn.net/weixin_42124234/article/details/102725470