我们都知道,int型变量占四个字节,取值范围在 -2147483648~+2147483647 之间,大致范围是 -2* 10^9 ~ +2* 10^9 ,如果这个范围仍不够我们的需求,可以使用 unsigned int型,它的范围会比 int 型大一倍,如果还无法满足,还可以考虑使用 long long(大致范围在 -9* 10^18~ +9* 10^18)或者 unsigned long long 。
当然这些并不是接下来我要介绍的重点,既然本文的标题叫做高精度数值运算,那么肯定有人会疑惑,什么是高精度数值运算?是把算数和结果精确到小数点后多少位那种么?显然不会是这样,所谓高精度数值运算,就是说参与运算的数值太大,大到什么程度呢?大到超出了编译器所能存储的基本数据类型的上限,即比unsigned long long 还要大的数据。
如此大的数据,该怎么运算呢?这就涉及到了高精度数值运算。
不过,在讲解运算之前,还要先解决一个问题:高精度数据的存储。
怎样存储高精度的数据?相信我们很多人都会不约而同地想到:数组!对,就是用数组。
这里我们定义一个数组 t[1000],因为高精度数据往往有很多位,为了使用方便,我们再定义一个变量len,用来存储长度,然后把它们封装成一个结构体,如下:
struct bign{
int t[1000];
int len;
};
为了方便对每一个结构体变量赋值,还可以对结构体这样定义:
struct bign{
int t[1000];
int len;
bign()//这里是一个初始化结构体的函数,函数名与结构体名相同,可以在每次定义结构体变量的同时进行初始化
{
memset(t,0,sizeof(t));//使用memset要添加<string.h>头文件
len=0;
}
}
另外,输入高精度数据可以用字符串输入,因此还需要一个转换函数,作用是把字符串转换成数组。
转换函数如下:
void shift(char str[])
{
bign a;//定义一个结构体数据
a.len=strlen(str);//获取长度
for(int i=0;i<a.len;i++)//遍历字符串
{
a.t[i]=str[a.len-i-1]-'0';//字符数据变整型 -'0' 倒序输入
}
return a;
}
注意:因为输入的字符串是最低位存储数值最高位,故倒序转换为数组后,数组内是最低位存储数值最低位,即个位。
再顺便介绍一下高精度数据之间的比较:
int compare(bign a,bign b)
{
if(a.len>b.len)
return 1;//a大
else if(a.len<b.len)
return -1;//a小
else
{
for(int i=a.len-1;i>=0;i--)
{
if(a.t[i]>b.t[i])
return 1;
else
return -1;
}
return 0;//一样大
}
}
好了,了解了上面的内容后,接下来正式介绍高精度数值运算。
这里仅介绍四种高精度运算,分别是:高精度加法运算,高精度减法运算,高精度数据与低精度数据的乘法运算,以及高精度数据与低精度数据的除法运算。至于高精度与高精度的乘法和除法,将在以后有机会介绍。
【高精度加法运算】
先看一个十分简单的例子:
1 2 5
+ 8 8
2 1 3
第一步:8+5=13>10 13-10=3 进位+1
第二步:2+8+1=11>10 11-10=1 进位+1
第三步:1+1=2<10 结束
将上面的步骤转换成算法的话,就是这种:
bign add(bign a;bign b)//因为需要返回一个结构体类型,故函数类型也为结构体型
{
bign c;
int carry=0;//进位设置为0;
for(int i=0;i<=a.len||i<=b.len;i++)//以较长的为基准
{
int temp=a.t[i]+b.t[i]+carry;
c.t[c.len++]=temp%10; //temp>10则只存储个位,temp<10则全部存入
carry=temp/10;//这里如果temp<10,则temp为0
}
if(carry!=0)//说明temp>10
{
c.t[c.len++]=carry;//注意,这里的意思是数组的第len位重新被赋值为carry,但因为carry>10,故len++
}
return c;
}
具体完整代码如下:
#include <stdio.h>
#include <string.h>
struct bign{
int t[1000];
int len;
bign()
{
memset(t,0,sizeof(t));
len=0;
}
};
bign shift(char str[])//转换函数
{
bign a;
a.len=strlen(str);//注意strlen与sizeof的区别
for(int i=0;i<a.len;i++)
{
a.t[i]=str[a.len-i-1]-'0';
}
return a;
}
bign add(bign a,bign b)//定义高精度加法函数
{
bign c;
int carry=0;
for(int i=0;i<a.len||i<b.len;i++)//逐位相加
{
int temp=a.t[i]+b.t[i]+carry;
c.t[c.len++]=temp%10;//这条语句为易错语句!9
carry=temp/10;
}
if(carry!=0)
{
c.t[c.len++]=carry;
}
return c;
}
void print(bign a)//结构体输出函数
{
for(int i=a.len-1;i>=0;i--)//注意这里是逆序输出
{
printf("%d",a.t[i]);
}
}
int main()
{
char str1[1000],str2[1000];
gets(str1);
gets(str2);
print(add(shift(str1),shift(str2)));
return 0;
}
【高精度减法运算】
例子就不说了,可以自己尝试着写一两个例子看一看,总之就是把最普通的步骤转换成了代码。
如下:
bign sub(bign a,bign b)
{
bign c;
for(int i=0;i<a.len||i<b.len;i++)
{
if(a.t[i]<b.d[i])
{
a.t[i+1]--;
a.t[i]+=10;
}
c.t[c.len++]=a.t[i]-b.t[i];
}
while(c.len-1>=1&&c.t[c.len-1]==0)//差为零,以及多个连续的差为零的情况
{
c.len--;
}
return c;
}
【高精度与低精度的乘法】
直接上代码:
bign multi(bign a,int b)
{
bign c;
int carry=0;
for(int i=0;i<a.len;i++)
{
int temp=a.t[i]*b+carry;
c.t[c.len++]=temp%10;
carry=temp/10;
}
while(carry!=0)
{
c.t[c.len++]=carry%10;
carry/=10;
}
return c;
}
【高精度与低精度的除法】
代码如下:
bign divide(bign a,int b,int &r)//r是余数,这里r采用引用方便传出r的值
{
bign c;
c.len=a.len;
r=0;
for(int i=a.len-1;i>=0;i--)
{
r=r*10+a.t[i];//余数*10与新位结合
if(r<b)
c.t[i]=0;//商设置为零
else
{
c.t[i]=r/b;//商作为值
r=r%b;//获得新的余数
}
}
while(c.len-1>1&&c.t[len-1]==0)//去零
{
c.len--;
}
return c;
}