大数处理——c++实现
本课题来自我的c++编程作业,文章利用大数处理类,类名:hugeNumber来对大数(编译器自定义的数值类型无法处理的数)进行四则运算(大数加法、大数减法及大数乘法的运算,除暂时没实现)和按精度四舍五入,自定义科学计数法等。内容广泛涉及运算符重载、字符连接、字符加减和字符乘除等作者原创函数。重要提示:本文涉及的所有函数使用的进制皆为10进制。(备注:已将该博客搬迁至CSDN)
一、解题思路
1 核心思想
文章用hugeNumber类对大数进行操作。前提假设,处理的数都使用科学计数法(未用科学计数法表示的大数,指数默认为0),hugeNumber类数据成员如下:
class hugeNumber{ //定义大数,10进制
private:
/*char deciNum[MAXSIZE]; */
char *deciNum; //尾数
long exponent; //指数
short sign; //符号位,取-1或1
int set_dot; //小数点位置,-1表示没有小数点
我将科学计数法分解为各个组成部分如图 1所示,其中:
①deciNum,科学计数法中的尾数,我用最大512字节表示该数;
②exponent,科学计数法中的指数,我用long int 表示;
③sign,整个数据的正负号;
④set_dot,当前尾数小数点真实位置,从0开始计数。
图 1
当从cin输入流获得一串字符,首先判断其是否为数值
if(!isNumber(nptr))
{
cout<<"This is not a number! " ;
exit(EXIT_FAILURE); //非正常退出
}
确定为数值后,hugeNumber类接受该字符串,并将其转换成如上所示4个属性,其构造函数原型为:
hugeNumber::hugeNumber(const char *nptr);
也可按提示,分别录入尾数部分、指数部分和正负号。其函数实现:
istream& operator>>(istream &in,hugeNumber &obj) //该函数声明为hugeNumber类友员函数
{
char ch;
in>>ch;
if(ch = '-')obj.sign = -1;
else obj.sign = 1;
in>>obj.deciNum;
in>>obj.exponent;
return in;
}
当我们获得某一hugeNumber类对象的各个属性值,便可以对该对象进行操作了。
2 大数按精度四舍五入
函数原型及方法如下:
void my_round(hugeNumber &hu ,int prec) //precision,规定的精度位
首先,对hugeNumber类的对象hu按指定自定义规格化。
norm(hu, -hu. exponent - prec);
该语句表示,将hu对象按指定精度规格化,并保持数值大小不变。例如,hu为3.14159e2,现在要求精确度为2,那么先将hu规格化为31415.9e-2。
接下来,判断小数点后一位是否大于0.5,如果大于0.5,给尾数个位加1,否则不加1,并且将小数点位的 ’.’ 修改为字符串结束符 ‘\0’,因为尾数使用字符串表示,所以规格化后的小数全部被截断。如图2所示
图2
2 大数加法运算
函数原型及基本算法思想描述:
2.1 两个整数大数相加
字符串a与字符串b整数字符串相加,加法考虑到进位,因为较大字符串a的2倍也只能产生1个进位,所有只预留1个进位给最终结果字符串c。
加法算法过程:
首先,判断a、b皆为整数,当hugeNumber类对象的数据成员set_dot为-1,表示该大数为整数;a、b对介后相加;c的指数为a、b对介后的指数。
其次,strlen(a)、strlen(b)获得a、b字符串长度,并确定保存结果的字符串长度,c字符串长度为a、b最长的长度加1(假设a是较长的大数)clength = strlen(a) + 1;
最后,从a、b字符串尾部开始从后往前相加,相当于从数的个位开始相加,如有进位,设置int变量 flag表述进位,flag最大为1,你知道为什么不能为2吗?待短字符串相加结束,将a(假设a是较长的大数)剩余的部位复制到c中,但仍然考虑与b累加的进位,如果a余下的是9999,切好又有进位,那么就要一直进位到数的最高位。如图 3所示。
图 3
2.2整数大数与带小数大数相加
整数大数与带小数大数相加,又分为两种情况:整数大数的整数部分大于带小数大数的整数部分和整数大数的整数部分小于带小数大数的整数部分。无论如何,a、b对介后才能相加;c的指数为a、b对介后的指数。所有这里尾数出现的整数和小数都是相当而已的,你完全可以把a、b都规格化成大整数,再相加。分别见图4 、图 5 。
图 4 整数大数的整数部分大于带小数大数的整数部分
图 5整数大数的整数部分小于带小数大数的整数部分
由图 4 所知,c字符串(保存最终结果)小数部分为b字符串小数部分,直接复制,b小数点前半段(b字符串整数部分)与整数大数a字符串相加同图 3 所示。
c的整个长度 = a 的整个长度 + b的小数长度 + 1 ;
图 5表示整数大数b的整数部分小于带小数大数a的整数部分,c的整个长度 = a 的整个长度 + 1 ;a的小数点后半段直接复制到c字符串中,a整数部分与整数大数b相加算法同图 3 所示。
2.3 两个带小数的大数相加
两个带小数的大数相加,分两张情况讨论:一种是a的整数部分大于b的整数部分,但a的小数部分短于b的小数部分,如图 6 所示。另一种情况,a无论是整数部分还是小数部分,都长于等于b,如图 7 所示。无论如何,a、b对介后才能相加;c的指数为a、b对介后的指数。所有这里尾数出现的整数和小数都是相当而已的,你完全可以把a、b都规格化成大整数,再相加。
图 6 字符串a的整数部分大于b的整数部分,但a的小数部分短于b的小数部分
图 6-1 补0处理,a、b字符可以从个位向高位依次相加
图 7 a无论是整数部分还是小数部分,都长于等于b
图 7-1 补0处理小数,a、b字符可以从个位向高位依次相加
3 大数减法运算
前提:无论如何,a、b对介后才能相减;c的指数为a、b对介后的指数。
3.1 两个整数大数相减
减法虽然没有溢出,但是会有正负,我用sign返回两个大数相减后的正负。因此,在做减法运算时,先判断结果的正负,在用较大的数减去较小的数。假设c = a – b;a > b,sign为正;当a < b,sign为负,如图 8 所示。
图 8两个整数大数相减
3.2 一个整数大数与另一个带小数大数间相减
减法虽然没有溢出,但是会有正负,同样用sign返回两个大数相减后的正负。此处有两种情形:一种是整数大数大于带小数的大数,另一种情形是整数大数小于带小数的大数。假设a是整数大数,b是带小数的大数。
图 9 整数大数大于带小数的大数
图 9-1 整数大数大于带小数的大数处理
第一种情形整数大数大于带小数的大数,先将整数大数a整数减 1,其小数 设为0.999+0.001;分别将0.999与b的小数相减,整数-1后与b的整数相减。见图9-1。
图 10整数大数小于带小数的大数
图 10-1 整数大数小于带小数的大数处理后相减
第二种情形整数大数小于带小数的大数,sign取-1,c = a – b变换为 c = -(b - a);
b 小数位直接复制给c,整数部分按照两个整数大数相减处理。
3.3 带小数大数间相减
有下列四种情形:
图 11 a的整数大于b,a的小数短于b
图 12 a的整数、小数都短于b
图 13 a的整数、小数都长于b
图 14 a的整数小于b,a的小数长于b
3 大数乘法运算
首先将a、b规格化为尾数为整数的大数,c的指数等于a、b规格化后的指数相加。有效位数长的做被乘数,位短的做乘数。假设a的长度大于b;
图 15
整个乘法过程,仿真实数据相乘,将所有中间结果累加到c中,如 c = tmp1 + tmp2 + tmp3 + … ;得到最终结果,由于博主比较懒,具体看算法实现。见图 15所示。
二、具体算法实现
1 #include <iostream> 2 #include <string> 3 #include <float.h> 4 5 #include "strSub.h" 6 #include "strMul.h" 7 #include "strAdd.h" 8 9 #define isdigit(c) ('0' <= (c) && (c) <= '9') 10 using namespace std; 11 const int MAXSIZE = 512; 12 13 class hugeNumber{ //定义大数,10进制 14 private: 15 /*char deciNum[MAXSIZE]; */ //尾数 16 char *deciNum; 17 long exponent; //指数 18 short sign; //符号位,取-1或1 19 int set_dot; //小数点位置,-1表示没有小数点 20 public: 21 hugeNumber() 22 { 23 this->deciNum = new char [MAXSIZE]; //开辟尾数空间 24 if(!this->deciNum ) 25 { 26 cout<<"allocation failure!"<<endl; 27 exit(EXIT_FAILURE); 28 } 29 } 30 hugeNumber(char deciNum[], long exponent, int set_dot = -1); 31 hugeNumber(const char *nptr); 32 hugeNumber(hugeNumber &b); //拷贝构造函数 33 ~hugeNumber(){delete deciNum;} 34 int getsign(){return sign;} 35 int getdot(){return set_dot;} 36 char *getdeciNum(){return deciNum;} 37 long getexponent(){return exponent;} 38 hugeNumber operator +(hugeNumber &b); 39 hugeNumber operator +(char str[]); 40 hugeNumber operator -(hugeNumber &b); 41 hugeNumber operator -(char str[]); 42 hugeNumber operator *(hugeNumber &b); 43 hugeNumber operator *(char str[]); 44 hugeNumber operator /(hugeNumber &b); 45 hugeNumber operator /(char str[]); 46 friend ostream& operator<<(ostream &out,hugeNumber &b); 47 friend istream& operator>>(istream &in,hugeNumber &b); 48 friend void norm(hugeNumber &hu); //按科学计数规格化,如12.34e5,规格化后1.234e6 49 friend void norm(hugeNumber &hu, long exp ); //按exp介规格化 50 friend void my_round(hugeNumber &hu ,int prec); //按精度四舍五入 51 52 53 }; 54 55 extern char * strAdd(char *c , const char *a, const char *b);//数值型字符串相加 56 extern void integerAdd(char *c,const char *a,const char *b,int k,int i,int j,int flag);//整数字符串数组相加 57 int isNumber(const char *nptr);//判断字符串是否符号数据特征 58 void expandArray(char a[], int n);//n表示数组头插入的元素个数 59 void jumpZero(char * nptr);//跳过尾数前面的无用的0,除了小数点前的 60 int findDot(const char *nptr);//返回小数点位 61 62 void expandArray(char a[], int n)//n表示数组头插入的元素个数 63 { 64 char *tmp = new char[MAXSIZE]; 65 if(!tmp) 66 { 67 cout<<"allocation failure!"<<endl; 68 exit(EXIT_FAILURE); 69 } 70 int j = 0; 71 for(; a[j] != '\0'; j++) 72 tmp[j+n] = a[j]; 73 tmp[j+1] = '\0'; 74 // tmp[0] = '1'; 75 for(j = 0; tmp[j] != '\0'; j++) 76 a[j] = tmp[j]; 77 delete tmp; 78 } 79 80 //跳过尾数前面的无用的0,除小数点前的0,【ps】数值小数点后尾部无效0暂时无法剔除 81 void jumpZero(char * nptr) 82 { 83 char *tmp = new char[MAXSIZE]; 84 if(!tmp) 85 { 86 cout<<"allocation failure!"<<endl; 87 exit(EXIT_FAILURE); 88 } 89 char *s = nptr; 90 while (*s == '0') 91 ++s; 92 int got_digit = 0; //得到1-9数字 93 int i = 0; 94 for(; *s != '\0' && *s - '0' < 10 && *s - '0' >= 0;)//从第一个不是0的数开始记录 95 { 96 tmp[i++] = *s++; 97 got_digit = 1; 98 } 99 if (*s == '.') 100 { 101 if (!got_digit) // if (!i) 尾数的整数部分全是0或没有 102 { 103 tmp[0] = '0'; 104 i = 1; 105 } 106 tmp[i] = '.'; 107 s++; 108 i++; 109 110 while ( *s != '\0' && *s - '0' < 10 && *s - '0' >= 0)//提取尾数的小数 111 { 112 tmp[i++] = *s++; 113 } 114 } 115 tmp[i] = '\0'; //标记尾数结束 116 i = 0; //回头 117 while(tmp[i] != '\0') //复制字符数组 118 nptr[i++] = tmp[i]; 119 nptr[i] = '\0'; //标记结束位 120 delete []tmp; 121 } 122 123 //返回小数点真实数组下标的位置 124 int findDot(const char *nptr) 125 { 126 const char *s = nptr; 127 int count = -1; 128 while(*s != '\0') 129 { 130 count++; 131 if(*s== '.') 132 return count; 133 s++; 134 } 135 return -1;//标识未找到小数点 136 } 137 138 ////返回一个对应的小写字母 139 char TOLOWER(const char *s) //传入字符指针,地址 140 { 141 char tmp; 142 if(*s >= 'A' && *s <= 'Z') 143 tmp = *s + 'a' - 'A'; 144 else if(*s >= 'a' && *s <= 'z') 145 ; 146 else 147 errno = EINVAL; //#define EINVAL 22 无效值异常 148 return tmp; 149 } 150 151 //判断是否是实数,此处仅仅处理10进制 152 int isNumber(const char *nptr) 153 { 154 const char *s = nptr; 155 156 if (nptr == NULL) //判断字符串是否为空 157 { 158 159 goto noNumber; 160 } 161 s = nptr; 162 while (*s == 32) //跳过前面的空格,正确 163 ++s; 164 if (*s == '-' || *s == '+') //碰到正负号,取下一个字符元素 165 ++s; 166 167 int flag = 1; //遇到非数值字符标记 168 int got_dot = 0; //获得圆点个数 169 int got_digit = 0;//0-9数字 170 171 for(; (*s != '\0') ;++s) 172 { 173 //if( (*s - '0'< 10) && (*s - '0'>= 0)) //是否是字符型数字 174 if(isdigit(*s)) 175 got_digit = 1 ; 176 else if (!got_dot && *s == '.') //没有遍历到小数点,又刚好碰到小数点时,设置函数状态 177 got_dot = 1; 178 else 179 break; //这个字符不是数值型的字符,可能碰见e 180 181 } 182 183 if (!got_digit ) //没有收集到数字 184 goto noNumber; 185 if( *s != '\0' && *s != 'e' && *s != 'E')//判断异常字符 186 goto noNumber; 187 if( *s == 'e'||*s == 'E')//没有遍历到E,又刚好碰到E时,设置函数状态 188 { 189 s++; 190 if (*s == '-' || *s == '+') //碰到正负号,取下一个字符元素 191 ++s; 192 for(;*s != '\0';++s) //指数是否合法 193 { 194 //if( (*s - '0'< 10) && (*s - '0'>= 0)) //是否是字符型数字 195 if(isdigit(*s)) 196 ; 197 else 198 goto noNumber; 199 } 200 201 } 202 return 1; 203 noNumber: 204 errno = EINVAL; //#define EINVAL 22 205 return 0; 206 } 207 208 //按科学计数规格化,如12.34e5,规格化后1.234e6 209 void norm(hugeNumber &hu) //大染缸 210 { 211 int i = findDot(hu.deciNum); //小数点位置 212 int k = 0; //标记首个非0数字位置, 213 214 char *s = hu.deciNum; 215 for(; *s - '0'== 0 || *s == '.';) // 跳过首字符0或小数点,直到扫描到第一个非0 216 { 217 ++s;++k; 218 } 219 //k++;//首个即非0也不是小数点,***k不能再加*** 220 if(i == -1)//尾数为整数 221 { 222 i++; 223 while(hu.deciNum[i])i++;//记录尾数长度 224 hu.deciNum[i] = '.'; 225 hu.deciNum[i+1] = '\0'; 226 while(i > 1) 227 { 228 hu.deciNum[i] = hu.deciNum[i-1]; 229 hu.deciNum[i-1] = '.'; //小数点前一位向右移一位 230 i--; 231 hu.exponent++; 232 } 233 234 } 235 else if (i-1 > k) 236 { 237 while(i-1 > k && i > 1)//小数点在首个非0数字后 238 { 239 hu.deciNum[i] = hu.deciNum[i-1]; 240 hu.deciNum[i-1] = '.'; //小数点前一位向右移一位 241 i--; 242 hu.exponent++; 243 } 244 } 245 else if(i < k) 246 { 247 248 while(i < k )//小数点在首个非0数字前 249 { 250 hu.deciNum[i] = hu.deciNum[i+1];//小数点与后一个数据交换位置 251 hu.deciNum[++i] = '.'; 252 hu.exponent--; 253 } 254 255 } 256 else 257 ; 258 jumpZero(hu.deciNum); 259 //========================================== 260 //剔除末尾是小数点 261 //========================================== 262 int j = 0; 263 while(hu.deciNum[j]!= '\0') //有效字符长度 264 j++; 265 if(hu.deciNum[j-1] == '.' ) //最后一位是小数点 266 { 267 hu.deciNum[--j] = '\0'; 268 hu.set_dot = -1; //没有小数点啦! 269 } 270 else 271 hu.set_dot = findDot(hu.deciNum);//找到小数点位置 272 } 273 274 //按exp介规格化 275 void norm(hugeNumber &hu, long exp ) //按exp介规格化 276 { 277 int i = hu.getdot(); //小数点位置 278 if(exp == hu.exponent)return; 279 else if(exp > hu.exponent)//小数点左移 280 { 281 while(exp > hu.exponent)//小数点左移 282 { 283 hu.exponent++; 284 if(i == -1)//没有小数点 285 { 286 int j = 0; 287 while(hu.deciNum[j]!= '\0') //有效字符长度 288 j++; 289 hu.deciNum[j] = '.'; 290 i=j;//记录小数点位置 291 hu.deciNum[++j] = '\0'; 292 } 293 if(i == 1) //例如1.234时 294 { 295 //char *tmp = new char [512]; 296 //int j = 0; 297 //for(; hu.deciNum[j] != '\0'; j++) 298 // tmp[j+1] = hu.deciNum[j]; 299 //tmp[j+1] = '\0'; 300 //tmp[0] = '0'; 301 //for(j = 0; tmp[j] != '\0'; j++) 302 // hu.deciNum[j] = tmp[j]; 303 //delete tmp; 304 expandArray(hu.deciNum,1);//数组头插入1个元素0 305 hu.deciNum[0] = '0'; 306 i++;//复制的小数点向右移动一位 307 } 308 hu.deciNum[i] = hu.deciNum[i-1]; 309 hu.deciNum[--i] = '.'; //小数点前一位向右移一位 310 311 } 312 } 313 else //当exp < hu.exponent 时,小数点右移 314 { 315 if(i == -1)//没有小数点 316 { 317 int j = 0; 318 while(hu.deciNum[j]!= '\0') //有效字符长度 319 j++; 320 while(exp < hu.exponent) //尾数*10,即尾数添加0 321 { 322 hu.deciNum[j++] = '0'; 323 hu.exponent--; 324 } 325 hu.deciNum[j] = '\0'; 326 } 327 else 328 { 329 330 while(exp < hu.exponent) //有小数点,尾数*10,小数点右移 331 { 332 333 hu.exponent--; 334 335 if(hu.deciNum[i+1] == '\0') //一次右移 336 { 337 hu.deciNum[i+1] = '0'; 338 hu.deciNum[i+2] = '\0'; 339 340 } 341 hu.deciNum[i] = hu.deciNum[i+1]; 342 hu.deciNum[++i] = '.'; 343 } 344 jumpZero(hu.deciNum); 345 } 346 347 } 348 //========================================== 349 //剔除末尾是小数点 350 //========================================== 351 int j = 0; 352 while(hu.deciNum[j]!= '\0') //有效字符长度 353 j++; 354 if(hu.deciNum[j-1] == '.' ) //最后一位是小数点 355 { 356 hu.deciNum[--j] = '\0'; 357 hu.set_dot = -1; //没有小数点啦! 358 } 359 else 360 hu.set_dot = findDot(hu.deciNum);//找到小数点位置 361 } 362 363 //对大数按规定精度,四舍五入 364 void my_round(hugeNumber &hu ,int prec) //precision,规定的精度位(不能再是大数) 365 { 366 int i = -1; //标记小数位,默认-1表示没有小数点 367 int k = 0; //标记首个非0数字位置, 368 norm(hu ,0); //按精度规整大数,数值大小不变 369 //cout<<"niming "<<hu<<endl;//正确 370 norm(hu , -prec); 371 //cout<<"niming "<<hu<<endl;//正确 372 373 char *s = hu.deciNum; 374 for(; *s - '0'== 0 || *s == '.';) // 跳过首字符0,扫描到第一个非0 375 { 376 ++s;++k; 377 } 378 i = findDot(hu.deciNum); //找到小数点位置 379 if(i == -1) //没有小数点 380 { 381 norm(hu); //大数规格化 382 return; //不需要处理,精度达不到 383 } 384 if(i < k ) // 首个出现的非0字符在小数点位后 385 { 386 errno = ERANGE; //数据下溢 387 hu.deciNum[0] = '0'; 388 hu.deciNum[1] = '\0'; //设置结束标识 389 hu.exponent = 0; 390 hu.set_dot = -1; 391 return; 392 } 393 else // 首个出现的非0字符在小数点位前 394 { 395 396 if(hu.deciNum[i+1] - '0' < 5) 397 { 398 hu.deciNum[i] = '\0';//小数点变结束位 399 hu.deciNum[i+1] = '\0'; 400 } 401 else //考虑进位 402 { 403 int j = i-1; //小数点的前一位(最后一个保留位) 404 hu.deciNum[i] = '\0'; //小数点变结束位 405 hu.deciNum[i+1] = '\0'; 406 int flag = 1; //进位标识 407 //if(hu.deciNum[j] > '9') 408 // flag = 1; 409 // hu.deciNum[j] %= 10; 410 // j--; 411 // while(flag) 412 // { 413 // if(hu.deciNum[j] > '9') 414 // flag = 1; 415 // hu.deciNum[j] %= 10; 416 // j--; 417 // } 418 419 do{ 420 hu.deciNum[j] += flag; 421 if(hu.deciNum[j] <= '9') 422 { 423 flag = 0; 424 } 425 else 426 { 427 hu.deciNum[j] -= 10; 428 flag = 1; 429 j--; 430 } 431 }while(flag && j >= 0); 432 433 if(flag && j < 0) //数据溢出最高位 434 { 435 //char *tmp = new char [512]; 436 //int j = 0; 437 //for(; hu.deciNum[j] != '\0'; j++) 438 // tmp[j+1] = hu.deciNum[j]; 439 //tmp[j+1] = '\0'; 440 //tmp[0] = '1'; 441 //for(j = 0; tmp[j] != '\0'; j++) 442 // hu.deciNum[j] = tmp[j]; 443 //delete tmp; 444 expandArray(hu.deciNum, 1);//数组头插入1个元素 445 hu.deciNum[0] = '1'; 446 } 447 448 } 449 //norm(hu);//大数规格化 450 } 451 452 } 453 454 hugeNumber::hugeNumber(hugeNumber &b) //拷贝构造函数 455 { 456 457 this->deciNum = new char [MAXSIZE]; //开辟尾数空间 458 if(!this->deciNum ) 459 { 460 cout<<"allocation failure!"<<endl; 461 exit(EXIT_FAILURE); 462 } 463 strcpy(this->deciNum, b.deciNum); 464 this->exponent = b.exponent; 465 this->sign = b.sign; 466 this->set_dot = b.set_dot; 467 //cout<<"你调用了复制构造函数"<<endl; 468 } 469 hugeNumber::hugeNumber(char deciNum[], long exponent,int set_dot) 470 { 471 if(!isNumber(deciNum)) 472 { 473 cout<<"This is not a number! " ; 474 exit(EXIT_FAILURE); //非正常退出 475 } 476 this->deciNum = new char [MAXSIZE]; //开辟尾数空间 477 if(!this->deciNum ) 478 { 479 cout<<"allocation failure!"<<endl; 480 exit(EXIT_FAILURE); 481 } 482 483 const char *s = deciNum; 484 s = deciNum; 485 while (*s == 32) //跳过前面的空格,正确 486 ++s; 487 this->sign = *s == '-' ? -1 : 1; //保存正负号 488 if (*s == '-' || *s == '+') //碰到正负号,取下一个字符元素 489 ++s; 490 int i = 0; 491 for(; *s != '\0' && *s - '0' < 10 ; i++)//遇到第一个不是数字的字符,停止 492 *++s; 493 if (*s == '.') 494 { 495 int set_dot = i; 496 } 497 strcpy(this->deciNum , deciNum); 498 this->exponent = exponent; 499 500 } 501 //========================== 502 //核心构造函数 503 //========================== 504 hugeNumber::hugeNumber(const char *nptr) 505 { 506 507 if(!isNumber(nptr)) 508 { 509 cout<<"This is not a number! " ; 510 exit(EXIT_FAILURE); //非正常退出 511 } 512 this->deciNum = new char [MAXSIZE]; //开辟尾数空间 513 if(!this->deciNum ) 514 { 515 cout<<"allocation failure!"<<endl; 516 exit(EXIT_FAILURE); 517 } 518 const char *s = nptr; 519 520 s = nptr; 521 while (*s == 32) //跳过前面的空格,正确 522 ++s; 523 sign = *s == '-' ? -1 : 1; //保存正负号 524 if (*s == '-' || *s == '+') //碰到正负号,取下一个字符元素 525 ++s; 526 while (*s == '0') //跳过前面的无用的0,除了小数点前的 527 ++s; 528 exponent = 0; //指数,默认指数为0 529 set_dot = -1; //获得圆点位置 530 int got_digit = 0; //得到0-9数字 531 int i = 0; 532 533 //while(*s - '0' < 10 && *s != '\0') //提取尾数的整数 534 //{ 535 // deciNum = deciNum*10 + (*s++ - '0'); 536 //} 537 //deciNum *= sign ; 538 for(; *s != '\0' && *s - '0' < 10 && *s - '0' >= 0; )//提取尾数的整数部分 for(; *s != '\0' && isdigit(*s) ; i++) ; for(; *s != '\0' && *s - '0' < 10 && *s != '.'; i++) 539 { 540 deciNum[i++] = *s++; 541 got_digit = 1; 542 } 543 544 //提取尾数的小数部分 545 if (*s == '.') 546 { 547 if (!got_digit) // if (!i) 尾数整数部分全是0或没有 548 { 549 deciNum[0] = '0'; 550 set_dot = 1; //小数点在第二位 551 i = 1; 552 } 553 else 554 { 555 set_dot = i; //小数点在i位 556 } 557 deciNum[set_dot] = '.'; 558 s++; 559 i++; 560 561 while ( *s != '\0' && *s - '0' < 10 && *s - '0' >= 0)//提取尾数的小数 for(; *s != '\0' && *s - '0' < 10 && TOLOWER(*s) != 'e'; i++) 562 { 563 deciNum[i++] = *s++; 564 } 565 } 566 deciNum[i] = '\0'; //标记尾数结束 567 //提取指数 568 if( *s == 'e'||*s == 'E')//刚好碰到E时,设置函数状态 569 { 570 s++; 571 int sign2 = 1 ; //指数可能是负数 572 sign2 = (*s == '-') ? -1 : 1; //保存正负号 573 if (*s == '-' || *s == '+') //碰到正负号,取下一个字符元素 574 ++s; 575 while( *s != '\0' ) //提取指数的整数 576 { 577 exponent = exponent*10 + (*s++ - '0'); 578 } 579 exponent *= sign2; 580 } 581 582 } 583 584 ostream& operator<<(ostream &out,hugeNumber &obj) //输出 585 { 586 if(obj.sign < 0)out<<"-"; 587 out<<obj.deciNum<<"e"<<obj.exponent<<endl; 588 return out; 589 } 590 591 istream& operator>>(istream &in,hugeNumber &obj) //输入 592 { 593 char ch; 594 in>>ch; 595 if(ch = '-')obj.sign = -1; 596 else obj.sign = 1; 597 in>>obj.deciNum; 598 in>>obj.exponent; 599 return in; 600 } 601 602 //大数相加运算 603 hugeNumber hugeNumber::operator +(hugeNumber &b) 604 { 605 606 if((exponent < b.exponent)) // 对介 607 norm(b,this->exponent); 608 else 609 norm(*this,b.exponent); 610 611 612 //cout<<"b符号= "<<b.getsign()<<endl; 613 //cout<<"尾数= "<<b.getdeciNum()<<endl; 614 //cout<<"小数点位置= "<<b.getdot()<<endl; 615 //cout<<"指数= "<<b.getexponent()<<endl; 616 617 618 hugeNumber c; //返回该对象 619 if(this->sign > 0 && b.sign > 0)//判断正负号 620 { 621 c.sign = 1; 622 strAdd(c.deciNum, this->deciNum, b.deciNum); 623 } 624 else if (this->sign < 0 && b.sign < 0) 625 { 626 c.sign = -1; 627 strAdd(c.deciNum, this->deciNum, b.deciNum); 628 } 629 else if(this->sign > 0 && b.sign < 0) 630 { 631 /*if(this->sign < 0) 632 c = b - *this; 633 if(b.sign < 0) 634 c = *this - b;*/ 635 int sgn = 1; 636 strSub(c.deciNum, this->deciNum, b.deciNum, sgn); 637 c.sign = 1; 638 } 639 else 640 { 641 int sgn = 1; 642 strSub(c.deciNum, b.deciNum, this->deciNum, sgn); 643 c.sign = -1; 644 } 645 646 //cout<<c.getdeciNum()<<endl; 647 c.exponent = this->exponent; 648 c.set_dot = findDot(c.deciNum); 649 650 return c; 651 } 652 653 //大数相加运算2 654 hugeNumber hugeNumber::operator +(char str[]) 655 { 656 if(!isNumber(str)) 657 { 658 cout<<"This is not a number! " ; 659 exit(EXIT_FAILURE); //非正常退出 660 } 661 hugeNumber b (str); 662 return(*this + b); 663 } 664 //大数相减运算 665 hugeNumber hugeNumber:: operator -(hugeNumber &b) 666 { 667 if((exponent < b.exponent)) // 对介 668 { 669 norm(b,this->exponent); 670 } 671 else 672 norm(*this,b.exponent); 673 674 hugeNumber c; //返回该对象 675 if(this->sign > 0 && b.sign > 0)//判断正负号 676 { 677 int sgn = 1; 678 strSub(c.deciNum, this->deciNum, b.deciNum,sgn); 679 c.sign =sgn; 680 } 681 else if (this->sign < 0 && b.sign < 0) 682 { 683 int sgn = 1; 684 strSub(c.deciNum, this->deciNum, b.deciNum,sgn); 685 c.sign = -sgn; 686 } 687 else if(this->sign > 0 && b.sign < 0) 688 { 689 strAdd(c.deciNum, this->deciNum, b.deciNum); 690 c.sign = 1; 691 } 692 else 693 { 694 strAdd(c.deciNum, this->deciNum, b.deciNum); 695 c.sign = -1; 696 } 697 698 //cout<<c.getdeciNum()<<endl; 699 c.exponent = this->exponent; 700 c.set_dot = findDot(c.deciNum); 701 702 return c; 703 704 } 705 //大数相减运算2 706 hugeNumber hugeNumber:: operator -(char str[]) 707 { 708 if(!isNumber(str)) 709 { 710 cout<<"This is not a number! " ; 711 exit(EXIT_FAILURE); //非正常退出 712 } 713 hugeNumber b (str); 714 return(*this - b); 715 } 716 717 //大数相乘运算 718 hugeNumber hugeNumber:: operator *(hugeNumber &b) 719 { 720 //if((exponent != b.exponent)) // 对介 721 // norm(b,this->exponent); 722 hugeNumber c; //返回该对象 723 int clen = strlen(this->deciNum)+strlen(b.deciNum)+1; 724 int i = 0; 725 for(; i<clen;i++) 726 { 727 c.deciNum[i] = '0'; 728 } 729 c.deciNum[i] = '\0'; 730 c.exponent = this->exponent + b.exponent; 731 int cexp = 0; 732 if(this->sign > 0 && b.sign > 0)//判断正负号 733 { 734 strMul(c.deciNum, this->deciNum, b.deciNum, cexp, clen); 735 c.sign = 1;c.exponent += cexp; 736 } 737 else if (this->sign < 0 && b.sign < 0) 738 { 739 strMul(c.deciNum, this->deciNum, b.deciNum, cexp, clen); 740 c.sign = 1;c.exponent += cexp; 741 } 742 else if(this->sign > 0 && b.sign < 0) 743 { 744 strMul(c.deciNum, this->deciNum, b.deciNum, cexp, clen); 745 c.sign = -1;c.exponent += cexp; 746 } 747 else 748 { 749 strMul(c.deciNum, this->deciNum, b.deciNum, cexp, clen); 750 c.sign = -1;c.exponent += cexp; 751 } 752 753 //cout<<c.getdeciNum()<<endl; 754 755 c.set_dot = findDot(c.deciNum); 756 757 return c; 758 759 } 760 //大数相乘运算2 761 hugeNumber hugeNumber:: operator *(char str[]) 762 { 763 if(!isNumber(str)) 764 { 765 cout<<"This is not a number! " ; 766 exit(EXIT_FAILURE); //非正常退出 767 } 768 hugeNumber b (str); 769 return((*this) * b); 770 } 771 772 ////大数相加运算 773 //hugeNumber hugeNumber::operator +(hugeNumber &b) 774 //{ 775 // if((exponent != b.exponent)) //以*this对象为基准,规格化对象b 776 // norm(b,this->exponent); 777 // hugeNumber c; 778 // int i = findDot(this->deciNum);//*this对象的尾数小数点位 779 // int j = findDot(b.deciNum);//b对象的尾数小数点位 780 // int k = c.set_dot = (i > j) ? i : j;//k取最长的整数位 781 // int min = (i < j) ? i -1 : j - 1; //短的整数位 782 // //尾数整数部分 783 // c.deciNum[k] = '.'; //确定小数点 784 // k--; 785 // while(k > 0) 786 // { 787 // int flag = 0; 788 // int j = k; 789 // 790 // c.deciNum[k] = this->deciNum[k] + (b.deciNum[k] - '0'); //错误 791 // if(c.deciNum[j] > '9') //考虑进位 792 // { 793 // while(flag && j >= 0) 794 // { 795 // c.deciNum[j] -= 10; 796 // flag = 1; 797 // j--; 798 // } 799 // if(flag && j < 0) //数据溢出最高位 800 // { 801 // expandArray(c.deciNum, 1);//数组头插入1个元素 802 // c.deciNum[0] = '1'; 803 // } 804 // } 805 // k--; 806 // } 807 // return c; 808 //} 809 810 //大数相加运算2 811 //hugeNumber hugeNumber::operator +(char str[]) 812 //{ 813 // if(!isNumber(str)) 814 // { 815 // cout<<"This is not a number! " ; 816 // exit(EXIT_FAILURE); //非正常退出 817 // } 818 // hugeNumber b (str); 819 // if((exponent != b.exponent)) //以*this对象为基准,规格化对象b 820 // norm(b,this->exponent); 821 // hugeNumber c(*this); 822 // int i = findDot(this->deciNum);//*this对象的尾数小数点位 823 // int j = findDot(b.deciNum);//b对象的尾数小数点位 824 // int k = c.set_dot = (i > j) ? i : j;//k取最长的整数位 825 // int min = (i < j) ? i -1 : j - 1; //短的整数位 826 // //尾数整数部分 827 // c.deciNum[k] = '.'; //确定小数点 828 // k--; 829 // while(k > 0) 830 // { 831 // int flag = 0; 832 // int j = k; 833 // 834 // c.deciNum[k] += (b.deciNum[k] - '0'); 835 // if(c.deciNum[j] > '9') //考虑进位 836 // { 837 // while(flag && j >= 0) 838 // { 839 // c.deciNum[j] -= 10; 840 // flag = 1; 841 // j--; 842 // } 843 // if(flag && j < 0) //数据溢出最高位 844 // { 845 // expandArray(c.deciNum, 1);//数组头插入1个元素 846 // c.deciNum[0] = '1'; 847 // } 848 // } 849 // k--; 850 // } 851 // return c; 852 //} 853 854 //ostream& operator<<(ostream &out,hugeNumber &obj) //输出 发在此文件提示无法访问对象.元素 855 //{ 856 // if(obj.sign < 0)out<<"-"; 857 // out<<obj.deciNum<<"e"<<obj.exponent<<endl; 858 // return out; 859 //} 860 // 861 //istream& operator>>(istream &in,hugeNumber &obj) //输入 862 //{ 863 // char ch; 864 // in>>ch; 865 // if(ch = '-')obj.sign = -1; 866 // else obj.sign = 1; 867 // in>>obj.deciNum; 868 // in>>obj.exponent; 869 // return in; 870 //} 871 872 //void isSPACE(const char *nptr) 873 //{ 874 // const char *s = nptr; 875 // s = nptr; 876 // while (*s == 32) //跳过前面的空格 877 // ++s; 878 // while(*s != '\0') 879 // printf("%c",*s++); 880 // 881 //} 882 //void TOLOWER(char &s) //传入字符 883 //{ 884 // if(s >= 'A' && s <= 'Z') 885 // s += 'a' - 'A'; 886 // else if(s >= 'a' && s <= 'z') 887 // ; 888 // else 889 // errno = EINVAL; //#define EINVAL 22 无效值异常 890 //} 891 892 893 894 //if(!isNumber(nptr))return ; 895 //if(my_strtod(nptr,NULL) < 1e15)return; 896 // 897 //long double 898 //strToDouble(const char str[],char ** endptr) 899 //{ 900 // register const char *s; 901 // short int sign; //符号位 902 // 903 // int got_dot; //n. 点,小圆点; 904 // int got_digit; //n. 数字; 手指,足趾; 一指宽; 905 // long double decimalNum; //十进制小数,尾数 906 // long int exponent; //指数,阶码 907 // 908 // if (str == NULL) //字符串为空,作异常处理 909 // { 910 // errno = EINVAL; //#define EINVAL 22 911 // goto noconv; 912 // } 913 // s = str; 914 // //while (ISSPACE (*s)) //跳过前面的空格 915 // // ++s; 916 // while (*s == 32)++s; 917 // 918 // //取数据首位符号位 919 // if (*s == '-' || *s == '+') 920 // sign = *s++ == '-' ? -1 : 1; 921 // 922 // decimalNum = 0.0; 923 // got_dot = 0; //是否有圆点句号 924 // got_digit = 0;//是否是0-9数字 925 // exponent = 0; //指数实际大小 926 // 927 // //处理e之前的实数 928 // for (;; ++s) 929 // { 930 // if( (*s - '0'< 10) && (*s - '0'>= 0)) //是否是字符型数字 931 // { 932 // got_digit = 1; 933 // 934 // if (decimalNum > DBL_MAX * 0.1) //DBL_MAX为double最大正数 935 // ++exponent; 936 // else 937 // decimalNum = (decimalNum * 10.0) + (*s - '0'); //累加数值 938 // 939 // if (got_dot) 940 // --exponent; 941 // } 942 // else if (!got_dot && *s == '.') //没有遍历到小数点,又刚好碰到小数点 943 // got_dot = 1; 944 // else 945 // break; 946 // } 947 // if (!got_digit) //没有收集到数字 948 // goto noconv; //报错处理 949 // 950 // 951 //}
三、算法测试
1 测试按精度四舍五入
1 int main() 2 { 3 char str[MAXSIZE]; 4 do{ 5 cout<<"请输入一个实数(退出请输入esc):"<<endl; 6 cin.clear(); //cin.fail()返回0; 7 cin.sync(); //清空流 8 cin.getline(str,sizeof(str)); 9 if(strcmp(str,"esc") == 0)exit(0); 10 }while(!isNumber(str) ); 11 12 hugeNumber a(str); 13 int prec = 0; 14 do{ 15 cout<<"\n请输入要保留的有效位数:"<<endl; 16 cin.clear(); //cin.fail()返回0; 17 cin.sync(); //清空流 18 cin.getline(str,sizeof(str)); 19 prec = atoi(str); 20 }while(!isNumber(str) && prec >= 0 ); 21 22 my_round(a,prec); 23 cout<<a<<endl; 24 25 //hugeNumber a("1234567890000000000e-4"); 26 //norm(a); 27 //cout<<a<<endl; 28 return 0; 29 }
该主函数的实验结果如图 16 所示:
图 16 大数按精度四舍五入
2 测试大数四则运算
//测试大数四则运算 int main() { char str[MAXSIZE]; do{ cout<<"请输入第一个实数(退出请输入esc):"<<endl; cin.clear(); //cin.fail()返回0; cin.sync(); //清空流 cin.getline(str,sizeof(str)); if(strcmp(str,"esc") == 0)exit(0); }while(!isNumber(str) ); hugeNumber a(str); do{ cout<<"请输入第二个实数(退出请输入esc):"<<endl; cin.clear(); //cin.fail()返回0; cin.sync(); //清空流 cin.getline(str,sizeof(str)); if(strcmp(str,"esc") == 0)exit(0); }while(!isNumber(str) ); hugeNumber b(str); hugeNumber c = a + b;//只能初始化调用复制构造函数 cout<<"输出结果为:"<<endl; cout<<"a + b = "<<(a + b)<<endl; cout<<"a - b = "<<(a - b)<<endl; cout<<"a * b = "<<(a * b)<<endl; }
实验结果如图 17 所示
图17 大数四则运算实验结果
该文为作者原创,版权归云南师范大学信息学院所有,引用请注明出处!索取全部源码,请加 qq: 292729249
若编译出现下列错误:error C4996: 'strcat': This function or variable may be unsafe.
属兼容问题,请参照:http://heyunhuan513.blog.163.com/blog/static/160204220153894725887/