本文为大大维原创,最早于博客园发表,转载请注明出处!!!
一、概述
C/C++中的int类型能表示的范围是-2E31-2E31–1。unsigned类型能表示的范围是0-2E32–1,即 0-4294967295。所以,int和unsigned类型变量,都不能保存超过10位的整数。有时我们需要参与运算的数,可能会远远不止10 位,例如,可能需要保留小数点后面100位(比如求π的值),那么,即便使用能表示很大数值范围的double变量,但是由于double变量只有64位,所以还是不可能达到精确到小数点后面100位这样的精度。double变量的精度也不足以表示一个100位的整数。一般我们称这种基本数据类型无法表示的整数为大数。如何表示和存放大数呢?在c语言下,我们可以用数组存放和表示大整数,一个数组元素,存放大数中的一位。而在c++中,使用标准库的string类型,使得大数问题的计算更加实用(没有最大值的限制),更加灵活(输入更加简洁方便),更加简单(可以方便的处理小数之间的运算)。
二、算法原理简单描述:
看如下大整数的加法运算:
answer每一位都是num1、num2和carry的和,因此,我们在输入加数和被加数的string之后,可以将内容进行一次反转,这样,answer[i]=num1[i]+num2[i]+carry[i-1]。反转的一个重要的原因是可以方便的将向前的进位和运算变为向后的进位运算,有利于充分发挥string的特点。在这里,我们可以使用<algorithm>头文件下的reverse()函数方便的实现string的内容反转。当运算完毕后,反转回来即可。
当加入小数点后,我们就需要考虑一些额外的问题--小数点的位置问题,笔者在此采用了如下的策略:将输入内容格式检查之后(使用了cctpe头文件),将一个数分为小数部分和整数部分,然后先运算小数部分,将得到的carry最后和整数部分一起运算,最后将两部分的和拼接在一起。
对于大数的减法问题,基本上是大数加法的一个逆运算过程,笔者不在细讲,看源代码就可以很容易的理解。
三、程序代码:
1 /* 2 大数的运算1--加法: 3 利用C++ string实现任意长度正小数、整数之间的加减法 4 作者:大大维 5 2017/5/5 6 */ 7 #include<iostream> 8 #include<string> 9 #include<cctype> 10 #include<algorithm> 11 using namespace std; 12 string sum(string,string,string,string); 13 string sub(string,string,string,string); 14 int main() 15 { 16 string num1,num2; 17 cout<<"Input num1 , num2:"<<endl; 18 cin>>num1>>num2; 19 string num11,num12,num21,num22; 20 //输入检查 21 //是否是小数的标志 22 bool num1Flag=false,num2Flag=false; 23 for(auto c:num1) 24 { 25 //由数字或者数字加一个.组成 26 if(!isdigit(c)||num1.empty()) 27 { 28 if(c=='.'&&!num1Flag) 29 { 30 num1Flag=true; 31 } 32 else 33 { 34 cout<<"num1: Please input correct form!!!"<<endl; 35 return 0; 36 } 37 } 38 } 39 for(auto c:num2) 40 { 41 if(!isdigit(c)||num2.empty()) 42 { 43 if(c=='.'&&!num2Flag) 44 { 45 num2Flag=true; 46 } 47 else 48 { 49 cout<<"num2: Please input correct form!!!"<<endl; 50 return 0; 51 } 52 } 53 } 54 55 //字符串分割{整数部分和小数部分) 56 if(num1Flag)//如果是小数 57 { 58 int i=0; 59 while(i!=num1.size()&&num1[i]!='.') 60 { 61 num11+=num1[i]; 62 ++i; 63 } 64 while(++i!=num1.size()) 65 { 66 num12+=num1[i]; 67 } 68 //用于.XXX或XXX.型输入的控制 69 if(num11.empty()) 70 num11+='0'; 71 if(num12.empty()) 72 num12+='0'; 73 } 74 else//如果是整数 75 { 76 num11=num1; 77 num12+='0'; 78 } 79 if(num2Flag)//如果是小数 80 { 81 int i=0; 82 while(i!=num2.size()&&num2[i]!='.') 83 { 84 num21+=num2[i]; 85 ++i; 86 } 87 while(++i!=num2.size()) 88 { 89 num22+=num2[i]; 90 } 91 //用于.XXX或XXX.型输入的控制 92 if(num21.empty()) 93 num21+='0'; 94 if(num22.empty()) 95 num22+='0'; 96 } 97 else//如果是整数str 98 { 99 num21=num2; 100 num22+='0'; 101 } 102 103 cout<<"The Sum result = "<<sum(num11,num12,num21,num22)<<endl; 104 cout<<"The Sub result = "<<sub(num11,num12,num21,num22)<<endl; 105 } 106 107 108 //加法 较长的整数部分 较长的小数部分 较短的整数部分 较短的小数部分 109 string sum(string strLong1,string strLong2,string strShort1,string strShort2) 110 { 111 //小数部分计算 112 if(strLong2.size()<strShort2.size()) 113 { 114 string strTemp=strLong2; 115 strLong2=strShort2; 116 strShort2=strTemp; 117 } 118 //补0 119 for(int i=strShort2.size(); i<strLong2.size(); ++i) 120 strShort2+='0'; 121 //反转字符串 122 reverse(strLong2.begin(),strLong2.end()); 123 reverse(strShort2.begin(),strShort2.end()); 124 //小数部分进行加法计算 125 string strRes2(strLong2.size(),'0'); 126 int carry=0;//进位 127 for(int i=0; i!=strLong2.size(); ++i) 128 { 129 int a=strShort2[i]-'0',b=strLong2[i]-'0'; 130 a=a+b+carry; 131 carry=a/10; 132 strRes2[i]=(a%10)+'0'; 133 } 134 //反转回来 135 reverse(strRes2.begin(),strRes2.end()); 136 137 138 //整数部分计算 139 if(strLong1.size()<strShort1.size()) 140 { 141 string strTemp=strLong1; 142 strLong1=strShort1; 143 strShort1=strTemp; 144 } 145 //反转字符串 146 reverse(strLong1.begin(),strLong1.end()); 147 reverse(strShort1.begin(),strShort1.end()); 148 149 150 string strRes1(strLong1.size(),'0'); 151 for(int i=0; i!=strShort1.size(); ++i) 152 { 153 int a=strShort1[i]-'0',b=strLong1[i]-'0'; 154 a=a+b+carry; 155 carry=a/10; 156 strRes1[i]=(a%10)+'0'; 157 } 158 for(int i=strShort1.size(); i!=strLong1.size(); ++i) 159 { 160 int b=strLong1[i]-'0'; 161 b+=carry; 162 carry=b/10; 163 strRes1[i]=b%10+'0'; 164 } 165 if(carry) 166 { 167 strRes1+=(carry+'0'); 168 } 169 //反转回来 170 reverse(strRes1.begin(),strRes1.end()); 171 172 173 //合并整数部分和小数部分 174 string strRes=strRes1+'.'+strRes2; 175 return strRes; 176 } 177 178 //减法 被减数的整数部分 被减数的小数部分 减数的整数部分 减数的小数部分 179 string sub(string strBjs1,string strBjs2,string strJs1,string strJs2) 180 { 181 //小数部分进行减法计算 182 int cntTemp=strBjs2.size()-strJs2.size(); 183 //补0 184 if(cntTemp<=0) 185 { 186 for(int i=cntTemp;i!=0;++i) 187 { 188 strBjs2+='0'; 189 } 190 } 191 else 192 { 193 for(int i=cntTemp;i!=0;--i) 194 { 195 strJs2+='0'; 196 } 197 } 198 199 //反转字符串 200 reverse(strBjs2.begin(),strBjs2.end()); 201 reverse(strJs2.begin(),strJs2.end()); 202 string strRes2(strBjs2.size(),'0'); 203 int carry=0;//进位 204 for(int i=0; i!=strBjs2.size(); ++i) 205 { 206 int a=strBjs2[i]-'0',b=strJs2[i]-'0'; 207 a=a-b-carry; 208 if(a>=0) 209 { 210 carry=0; 211 strRes2[i]=a+'0'; 212 } 213 else 214 { 215 carry=1; 216 strRes2[i]=a+10+'0'; 217 } 218 } 219 //反转回来 220 reverse(strRes2.begin(),strRes2.end()); 221 222 223 224 //整数部分进行减法计算 225 //反转字符串 226 reverse(strBjs1.begin(),strBjs1.end()); 227 reverse(strJs1.begin(),strJs1.end()); 228 string strRes1(strBjs1.size(),'0'); 229 for(int i=0;i!=strJs1.size();++i) 230 { 231 int a=strBjs1[i]-'0',b=strJs1[i]-'0'; 232 a=a-b-carry; 233 if(a>=0) 234 { 235 carry=0; 236 strRes1[i]=a+'0'; 237 } 238 else 239 { 240 carry=1; 241 strRes1[i]=a+10+'0'; 242 } 243 } 244 for(int i=strJs1.size();i!=strBjs1.size();++i) 245 { 246 int a=strBjs1[i]-'0'; 247 a=a-carry; 248 if(a>=0) 249 { 250 carry=0; 251 strRes1[i]=a+'0'; 252 } 253 else 254 { 255 carry=1; 256 strRes1[i]=a+10+'0'; 257 } 258 } 259 if(carry)//此时除数比被除数大,结果为负数 260 { 261 return "*"; 262 } 263 //反转回来 264 reverse(strRes1.begin(),strRes1.end()); 265 266 267 //清楚冗余0 268 string strTemp; 269 cntTemp=0; 270 for(int i=0;strRes1[i]=='0';++i)++cntTemp; 271 for(int i=cntTemp;i!=strRes1.size();++i) 272 strTemp+=strRes1[i]; 273 strRes1=strTemp; 274 //合并整数部分和小数部分 275 string strRes=strRes1+'.'+strRes2; 276 return strRes; 277 }
四、运行结果截图:
说明1:此处有一定的容错性,可以处理(.X或X.型的数据)
说明2:(*)表示结果为负数,不再处理
说明3:对输出格式统一控制为小数类型