高精度之旅
---by ysmor
---本文为作者原创,请勿转载
在c++编程时,系统本来是带有运算符+,-,*,/,%的。但是,这些运算符也不是万能的。他们最多也就算个long long int 范围的加法罢了。但是,如果来一个10000位的数字加10000位的数字怎么办?
如果还用+号的话,系统就开始打乱码了。于是,一个人就发明了高精度算法。
高精度算法简介
高精度算法,属于处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精度数,高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘,开方等运算。对于非常庞大的数字无法在计算机中正常存储,于是,将这个数字拆开,拆成一位一位的,或者是四位四位的存储到一个数组中,用一个数组去表示一个数字,这样这个数字就被称为是高精度数。高精度算法就是能处理高精度数各种运算的算法,但又因其特殊性,故从普通数的算法中分离,自成一家。
----摘自百度百科
如果你要看不明白,那么我总结一下,就是把两个数通过计算机模拟生活中我们手算加法,像列竖式一样。
(PS:不会有人不会列竖式吧!!!T_T)
所以,正题开始了。。。。。。
首先,先想想这几个数的类型。int和long long int肯定不行。10000位呢。所以,我们可以用字符串string来解决这个问题。
(PS:如果你的dev-c++的版本比较久的话,请加入头文件#include<cstring>)
所以,基本程序如下
#include<iostream>
using namespace std;
int main()
{
string str1,str2,str3;
cin>>str1>>str2;
int len1=str1.length(),len2=str2.length();
/*
首先,介绍一下字符串:字符串和字符数组(char a[])一样,都可以用cin>>输入。str1,str2用来输入,而str3用来表示结果。
接着是len1,len2。这两个数用来表示字符串str1,str2的长度。
函数.lenth()表示测一个字符数组的长。例:str1.lenth()表示字符串str1
的长度。*/
如上图,这是一个加法竖式。虽然简单,但可以解释我们的程序。我们的竖式都是从最后一位开始算起的。如果有进位的话,可以进到下一位。
所以,据此我们采用反码来计算。所谓反码,就是把输入的数倒过来,这样的话,就可以从第一位开始,一位一位的算了。
对啦,忘说了,再定义一个jw代表进位。当进位时,把jw定义成进位的那个数,下次使用。
以下为反码所用的代码:
for(int i=0;i<len1/2;i++)swap(str1[i],str1[len1-i-1]);
for(int i=0;i<len2/2;i++)swap(str2[i],str2[len2-i-1]);
这样的话,我们就把正常的字符串转换成它的反码了。
解释一下,str1[i]代表前面的数,str1[len1-i-1]则是str1[i]所对应的反码。swap(a,b)代表交换a,b的值。这样的话,就把整个字符串都反过来了。
例如输入1234;它的反序就是4321
然后判断一下长短。如果str1比str2短,那么交换它们
代码:
if(len1<len2)
{
for(int i=0;i<len2;i++)swap(str1[i],str2[i]);
swap(len1,len2);
}
这样就确定str1比str2大了。
接下来是最精彩的部分:模拟手算竖式。先给代码:
int jw=0;
for(int i=0;i<len2;i++)
{
if(i<len1)
{
str3[i]=(str1[i]+str2[i]-'0'-'0'+jw)%10+'0';
jw=(str1[i]+str2[i]-'0'-'0'+jw)/10;
}
else if(i>=len1&&i<len2)
{
str3[i]=(str2[i]-'0'+jw)%10+'0';
jw=(str2[i]-'0'+jw)/10;
}
}
if(jw!=0)cout<<jw;
for(int i=len2-1;i>=0;i--)cout<<str3[i];
system("pause");
return 0;
}
return 0;
int jw=0;//这个刚才说过,定义的进位;
for(int i=0;i<len2;i++)
/*这个就是开始运算,从最后一位(刚才已经把它换为反码了)开始运算,跟最长的字符串一样长。*/
if(i<len1)
{
str3[i]=(str1[i]+str2[i]-'0'-'0'+jw)%10+'0';
jw=(str1[i]+str2[i]-'0'-'0'+jw)/10;
}
else if(i>=len1&&i<len2)
{
str3[i]=(str2[i]-'0'+jw)%10+'0';
jw=(str2[i]-'0'+jw)/10;
}
/*我想这句话可以好好讲讲:str3[i]为结果的第i位。
写得仔细一些,这句话可以拆成:
str3[i]=((str1[i] -'0')+(str2[i] -'0')+jw)%10+'0';
首先,str1[i]-'0'表示小的那个字符串的第i位。因为str1[i]为char形式,所以由ascii码可知,用它减一个字符'0'就可以得到他的int形式了。
同理,str2[i]-'0'表示大的字符串的第i位。
为什么要加jw?这里,因为是循环,所以jw是表示上一位的进位。加上才能表示这一位。%10表示这位万一要进位,就可以通过这种方法来确定这位是什么。
所以,按理说这已经算完了。但,不要忘记str3是字符串,所以要返回的是字符,所以不要忘记加上一个字符'0'。
但是,为什么开始会有一个if?
现在解释:当i<len1时,有两个数相加,所以是str1[i]+str2[i];
当i>=len1时,只有str2一个数自己加进位了,所以不用考虑str1[i];
*/
jw=(str1[i]+str2[i]-'0'-'0'+jw)/10;
/*
这段表示的是新一次进位。因为int在除以10的时候,如果除不尽,小数点后的所有都抹了。所以用这个来计算进位。这样返回的就是进位了。
*/
主体结构已经讲完,接下来就是输出。在输出前,有一个很重要的事情,就是先考虑最后一次进位。如果进位是0;就不用输出,不是零就输出。所以判断一下。
if(jw!=0)cout<<jw;
for(int i=len2-1;i>=0;i--)cout<<str3[i];
最后循环输出结果即可。
以下为正确代码:
正确代码:
#include<iostream>
using namespace std;
int main()
{
char str1[10001],str2[10001],str3[10001];
cin>>str1>>str2;
int len1=strlen(str1),len2=strlen(str2);
if(len1>len2)
{
for(int i=0;i<len1;i++)swap(str1[i],str2[i]);
swap(len1,len2);
}
for(int i=0;i<len1/2;i++)swap(str1[i],str1[len1-i-1]);
for(int i=0;i<len2/2;i++)swap(str2[i],str2[len2-i-1]);
int jw=0;
for(int i=0;i<len2;i++)
{
if(i<len1)
{
str3[i]=(str1[i]+str2[i]-'0'-'0'+jw)%10+'0';
jw=(str1[i]+str2[i]-'0'-'0'+jw)/10;
}
else if(i>=len1&&i<len2)
{
str3[i]=(str2[i]-'0'+jw)%10+'0';
jw=(str2[i]-'0'+jw)/10;
}
}
if(jw!=0)cout<<jw;
for(int i=len2-1;i>=0;i--)cout<<str3[i];
system("pause");
return 0;
}
感谢收看!!!
by---ysmor
2016.5.23