大數處理——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/
