高精度算法:做一個支持正負、小數高精度運算的C++類

最終效果:
結果預覽

一、算法理論

1.概述
2.儲存大數字
3.1乘法運算
3.2加法運算
3.3減法運算
3.4除法運算

二、類的構思

三、完整代碼

四、拓展想法


一、算法理論

(第一部分旨在說明思路,完整的代碼會在第三部分給出)

1. 概述
基本數據類型無法對過大的數據進行精確存取與運算,所以我們需要用高精度算法來對這些大數字進行運算。高精度算法需要考慮兩個問題:①如何存儲 ②如何運算。

先對於後者,我們可以選擇豎式運算,這可以把大數字的運算轉換成若干步小數字運算;
所以相應的,對於前者,我們需要選擇一種適合豎式運算的存儲方式——數組。豎式運算按數位運算,而數組可以把大數字按數組存儲。

2.1儲存大數字:正整數
用數組存儲數字的方式我建議以具體運算而改變。因爲某些運算的運算的開始點不同。
比如加法,是從最低位開始加的(如在豎式中,200+4是先個位的0和4相加);
而除法,是從最高位開始除的(如200/25是25先嚐試和最百位的2相除,不夠除再向十位借位除……)
但存儲是爲了運算的,所以應該選擇方便運算的方式。

舉個例子:

int a[101];

如果我要儲存12345,且我要進行加法,那麼我存儲在數組裏面就應該是(這裏a[0]爲了方便而忽略):

a[1] a[2] a[3] a[4] a[5]
5 4 3 2 1

這樣做可以方便相加後對高位進行進位

而如果我要進行除法,那麼就應是:

a[1] a[2] a[3] a[4] a[5]
1 2 3 4 5

這樣做可以方便相除後對低位借位或求商

然而這麼做問題又來了,一個數組可能存儲的是順序或者倒序數字,那麼我怎麼知道我存儲的是12345還是54321?因此我們還需要一個字符串來以字符形式把數字存儲起來,在需要運算時再把它以恰當的規則轉換至數組,而且這個字符串應該先用某些方法排除前導0和後導0(也只是爲了方便,或者你有更好的儲存方式也是可以的)。如果我們想存儲012345,則應該想辦法把最前面的0去掉,再賦值給字符串:

#include <string>
string numString = "12345";

2.2存儲大數字:小數及負數
小數與負數會產生一些特殊符號,無法存儲至數組中,那是否也應該存儲至字符值中呢?
我的建議是不要(因爲我試過,這樣會極大地增加字符串處理的難度),而是新建兩個變量,一個存儲正負號,一個存儲小數的長度,而字符值只存儲“純數字”(去除所有符號,所有前導0後導0的值)

舉個例子:
我要存儲-123.45,那麼就應該:

int sign = -1;//正負
int decimal = 2;//小數位
string numString = "12345";//純數字

2.3存儲大數字:字符純數字補充說明
(這只是我使用的一種儲存方式,如果有更好的想法可自行改良)
numString中儲存的應該是無符號除前導0後導0值,但保留小數位的所有有效0
下面一組例子能更清晰地讀懂這句話:

數字 numString值
2 2
02 2
2.0 2
0.2 2
-2 2
-2.3 23
0.002 002(保留兩個有效0)

0.002寫成002可以使字符串輸出時簡單一些,但是會導致運算的時候(特別是除法)比較複雜

2.4儲存大數字:總結
所以我們需要一些數組容器用於運算,一些變量示意正負與小數,一個字符串存儲數字的純數字字符值
當然如果我們需要輸出完整的數字還需要一個額外的函數來組裝數字爲字符串
爲方便運算起見這裏還多儲存了幾個新的變量

#include <string>
//運算時存儲
int digits1[101];//運算容器1
int digits2[101];//運算容器2
int digitsR[101];//結果容器
//平時存儲
int sign = -1;//正負
int length = 5;
int integer = 3;
int decimal = 2;//小數位
string numString = "12345";//純數字
注意:這裏列出的並非所有要用到的變量,爲了方便我還增加了一些變量
int integer;//整數位
int length;//長度
//獲取完整數字的函數
string GetFullNumber(){//...};

3.1.1運算:乘法:數組容器處理
和加法一樣,乘法從低位開始相乘,所以應該倒序存放於數組運算容器中。

3.1.2運算:乘法:正負號處理
乘法的正負號好辦,直接用兩個因數的符號相乘就好了

3.1.3運算:乘法:小數處理

  • 運算:無需處理
  • 結果:乘法的小數也好辦,直接用兩個因數的小數相加再排除後導0就好了

3.1.4運算:乘法:純數字處理
模擬乘法豎式,每位進行相乘,但結果應該存儲在哪一位?其實不難找到規律,
個位x個位=個位;
個位x十位=十位;
十位x十位=百位;
所以第i位與第j位相乘,結果將存儲於第i+j-1位

另外還需考慮到進位問題,如果出現進位,將增加i+j位的結果值,而i+j-1位只存其與10求餘的餘數。

舉個例子:
18x8=144
先是個位x個位,結果在個位;出現進位,十位要進6,而個位要保留個位結果與10取餘的結果4。
再是個位x十位,結果在十位;由於剛剛十位已經有6,十位也溢出需要進位,百位進位至1,十位保留與10取餘的結果4

1 8
8
1(進位1) 4(進位6+1x8)%10=4) 4(8x8%10=4)

運算次數:遍歷完每個因數
實現爲代碼則如下:

//length1和length2是兩個因數的長度
for (int secondDigit = 1; secondDigit <= length1; secondDigit++)//遍歷到豎式的第二個數
		{
			for (int firstDigit = 1; firstDigit <= length2; firstDigit++)//遍歷到豎式的第一個數
			{
				//模擬豎式中的某兩位數相乘
				digitsR[firstDigit + secondDigit - 1] += digits1[firstDigit] * digits2[secondDigit];
				//處理進位
				digitsR[firstDigit + secondDigit] += digitsR[firstDigit + secondDigit - 1] / 10;
				digitsR[firstDigit + secondDigit - 1] = digitsR[firstDigit + secondDigit - 1] % 10;
			}
		}

值得注意的是那兩個+=很關鍵,不要漏掉了+!

3.2.1運算:加法:數組容器處理
加法從低位開始相加,所以應該倒序存放於數組運算容器中。

3.2.2運算:加法:正負號判斷與處理

  • 加數同號:可以使用加法豎式進行相加,結果與加數同號
  • 加數異號:交給減法處理或再寫一次減法的邏輯(詳見減法部分,實現方法類似)

3.2.3運算:加法:小數處理

  • 運算:需要在存入運算容器前對齊,比如12.3+4,2應該與4對齊
十分
1 2 3
4 0

換句話說,存儲在數組裏面的應該是321和04

digits1[1] digits1[2] digits1[3]
3 2 1
十分
digits2[1] digits2[2] digits2[3]
0 4 0
十分
  • 結果:小數先取兩個加數中較長的,再排除後導0

3.2.4運算:加法:純數字處理(僅同號相加,異號參考減法思路)
i位相加存於結果i位中,若要進位則i+1位結果+1,i位結果與10取餘
運算次數LoopTime = 兩個加數的較長整數位+較長小數位

for (int i = 1; i <= loopTime; i++)
{
	digitsR[i] += digits1[i] + digits2[i];
	digitsR[i + 1] += digitsR[i] / 10;
	digitsR[i] = digitsR[i] % 10;
}

3.3.1運算:減法:數組容器處理
減法從低位開始相減,所以應該倒序存放於數組運算容器中。

3.3.2運算:減法:正負號判斷與處理

  • 同號:大減小,結果符號結合減數大小判斷
    被減數大:結果符號與減數一致
    減數大:結果符號與減數(或被減數)相反
  • 異號:交給加法處理或再寫一次加法邏輯

3.3.3運算:減法:小數處理
(同加法一樣,詳見加法的小數處理)

  • 運算:需要對齊
  • 結果:取減數與被減數較大者,再排除後導0

3.3.4運算:減法:數字比較
減法豎式需要選出較大的數,比如5-7=-2,應該是較大的7減去5,再修正結果符號爲負
由於我們使用字符串類來存儲“純數字”,所以比較的代碼比較簡短

//integer1和integer2爲兩個數的整數位
//numString是兩個string類字符串
if (integer1 > integer2)
			return 1;
		else if (integer1 < integer2)
			return -1;
		else if (numString1 > numString2)
			return 1;
		else if (numString1 == numString2)
			return 0;
		else 
			return -1;

3.3.5運算:減法:純數字處理(僅同號相減,異號參考加法思路)
第i位相減,借位時需要向被減數借位,此時第i位+10,而第i+1位的減數-1
運算次數LoopTime = 兩個加數的較長整數位+較長小數位
注意:這裏要確保digits1存的數一定比digits2要大,這點可配合上一步實現

for (int i = 1; i <= loopTime; i++)
{
	//digits1的值一定要比digits2要大!!!
	if (digits1[i] + digitsR[i] >= digits2[i])//在可能的借位後還夠不夠減?
		digitsR[i] += digits1[i] - digits2[i];
	else
	{
		digitsR[i + 1]--;
		digitsR[i] += 10 + digits1[i] - digits2[i];
	}
}

3.4.1運算:除法:數組容器處理
除數和被除數的處理和以上的不同,應該以順序存放於數組(詳見2.1)

3.4.2運算:除法:正負號處理
兩符號相乘即可

3.4.3運算:除法:小數處理

  • 運算:如除數爲0.02這種,要想辦法跳過多餘的0,只把2錄到運算數組中,否則會影響後面的試商
  • 結果:注意除數的小數也會導致結果小數位改變

3.4.4.1運算:除法:純數字處理:簡述
高精度除法是用減法模擬乘法試商,比如10/2=5,我們在列豎式的時候會用1~9乘2去試商,從而得出某數位的商。然而在計算機進行乘法試商計算量並不少,而且會用到多次高精度除法,所以我們可以選擇用減法來代替乘法。
舉個例子:對於10/2(可能需要聯想一下除法豎式進行的過程)
1先與2對齊,由於不夠減,於是除數移動到下一位
0就與2對齊,由於此時有十位的借位1,於是可以進行相減,被除數由10變爲8,個位商+1=1
8就與2對齊,可以相減,被除數由8變6,個位商+1=2
……
不夠減就執行到下一位
直到執行到除數運算容器的末尾爲止

3.4.4.2運算:除法:純數字處理:引入標記變量(這步只是爲了方便說明後面的步驟)
爲了減法試商,我們需要準確地獲取當前是哪個數位在進行試商。爲了方便起見這裏對處理除法的函數再引入三個變量

int checkPointFront = 1;//當前試商時被除數的最前檢查點(用於減法試商前比較的標記)
int checkPointTrail = 除數的長度;//當前試商時被除數的最後檢查點(用於相減的標記)
int divisorcheckTrail = 除數的長度;//記錄試商時除數的最後檢查點(用於相減的標記)

比如三個變量記錄的位置依次如圖所示:
三個變量記錄的位置依次如圖所示
其中①和②每輪試商完畢之後都會向後跳一位
具體作用會在後面兩步體現

3.4.4.3運算:除法:純數字處理:試商比較
怎麼才能知道當前數位的試商已經結束(被除數當前位不夠減了)?顯然我們又需要進行判斷,並且每減一次之前都要執行這個判斷
這是一個被除數的局部數字和除數進行比較,顯然之前減法提及的數字比較不適用

//先假定可以減
bool canSub = true;
//判斷是否能夠進行減法試商(夠不夠減)
if (digits1[checkPointFront - 1] == 0)//如果沒有借位才判斷,否則直接跳出
{
	//看看夠不夠減
	for (int i = checkPointFront, j = 1; i <= checkPointTrail; i++, j++)
	{
		//如果大於,則肯定夠減,直接跳出
		if (digits1[i] > digits2[j])
		{
			break;
		}
		//如果執行到這一句,唯一的情況就是前若干位都等於,且這一位小於,因此整體值爲小於,不能減
		if (digits1[i] < digits2[j])
		{
			canSub = false;
			break;
		}
	}
}

最後可以得到一個準確的判斷結果

3.4.4.4運算:除法:純數字處理:減法試商
整體思路:

//遍歷到運算數組結束爲止
//checkPointTrail恰好也指向當前試商結果的位置
//checkPointFront, checkPointTrail都要在每次循環結束後往後跳一
for (checkPointTrail; checkPointTrail <= 100; checkPointFront++, checkPointTrail++)
		{
			while (1)
			{
			
				這裏插入上面的比較代碼,得到一個canSub值
				
				if (canSub)
				{
					進行減法試商,相減後該位的商+1
					具體代碼詳見下面
				}
				else
				{
					break;//跳出循環,代表試商結束
				}
			}
		}

減法試商具體代碼:

for (int i = checkPointTrail, j = divisorcheckTrail; j > 0; i--, j--)
{
	//如果直接能減那就減
	if (digits1[i] >= digits2[j])
		digits1[i] -= digits2[j];
	else
	{
		//否則進行借位減法
		digits1[i - 1]--;
		digits1[i] = 10 + digits1[i] - digits2[j];
	}
}
digitsR[checkPointTrail]++;//商++ 這是我們最終的目標

當大循環執行完畢後,就可以得到商的完整的結果
而此時digits1會變成全爲0的數組,所以這個除法運算是會改變原數組的,這也是爲什麼要把數字值存儲在string中,而不存在數組中的原因之一

二、類的構思

首先定義一個Number類

class Number{//包含以下的成員...};

需要使用字符串類

#include <string>

需要使用變量來記錄大數字

int sign = 1;
int length = 0;
int integer = 0;
int decimal = 0;
string numString;//儲存無符號的數字(同時去除前導0和後導0,但保留小數位的有效0)

需要有3個運算容器用於二元運算及結果
考慮到所有類共用3個容器,且容器需要在第一次使用時初始化爲0,這裏定義爲static變量
容器的大小決定了最大運算範圍,這裏101代表最多可進行約50位數×50位數的乘法運算

static int digits1[101];
static int digits2[101];
static int digitsR[101];
//注意static成員在類外需要再定義一次

需要有錄入數字的構造函數,考慮到實際使用,這裏定義了多個構造函數
函數的具體代碼參考後面的完整代碼

Number(const string& inputString){//...}

Number(const int& inputNumber){//...}

Number(const long long int& inputNumber){//...}

//這個函數的實現精度只有大概6位,太長的浮點數應用字符串錄入
Number(const double& inputNumber){//...}

//用於運算結果的Number對象構造,包含對-0的修正
Number(int resultSign, int resultLength, int resultInteger, int resultDecimal, string resultString){//...}

需要獲得完整數字(字符串)的函數
函數的具體代碼參考後面的完整代碼

string GetFullNumber(){//...}

需要有Number類的四則運算符重載
函數的具體代碼參考後面的完整代碼

Number operator + (const Number& addend){//...}
Number operator - (const Number& subtractor){//...}
Number operator * (const Number& factor){//...}
Number operator / (const Number& divisor){//...}

需要有若干個類內部運算所需的private函數

三、完整代碼

(運算符重載中部分又長又臭的代碼可能在第一部分有解釋到)

#include <iostream>
#include <string>

using namespace std;

class Number
{
public:
	//運算容器
	static int digits1[101];
	static int digits2[101];
	static int digitsR[101];
	//數字屬性
	int sign = 1;
	int length = 0;
	int integer = 0;
	int decimal = 0;
	string numString;//儲存無符號的數字(同時去除前導0和後導0,但保留小數位的有效0)

	Number(const string& inputString)
	{
		constructNumber(inputString);
	}

	Number(const int& inputNumber)
	{
		string tempString = to_string(inputNumber);
		constructNumber(tempString);
	}

	Number(const long long int& inputNumber)
	{
		string tempString = to_string(inputNumber);
		constructNumber(tempString);
	}

	//精度只有大概6位,太長的浮點數應用字符串錄入
	Number(const double& inputNumber)
	{
		string tempString = to_string(inputNumber);
		constructNumber(tempString);
	}

	//用於運算結果的Number對象構造,包含對-0的修正
	Number(int resultSign, int resultLength, int resultInteger, int resultDecimal, string resultString)
	{
		if (length == 1 && resultString[0] == '0')
			sign = 1;
		else
			sign = resultSign;
		length = resultLength;
		integer = resultInteger;
		decimal = resultDecimal;
		numString = resultString;
		
	}



	//用於獲取函數的字面字符串值
	string GetFullNumber()
	{
		string outString;
		if (sign == -1)
		{
			outString += "-";

		}
		for (int i = 0; i < length; i++)
		{
			if (i == length - decimal)
			{
				if (i != 0)
					outString += ".";
				else
					outString += "0.";
			}
			outString += numString[i];

		}
		return outString;
	}

	Number operator + (const Number& addend)
	{
		additionSort(addend);

		int resultSign;
		int resultLength;
		int resultDecimal;
		string resultString;

		int addIndex;
		int loopTime;

		resultDecimal = decimal > addend.decimal ? decimal : addend.decimal;//先取兩者最大
		loopTime = (integer > addend.integer ? integer : addend.integer) + resultDecimal;//最大小數+最大整數
		resultLength = loopTime + 1;//先取足夠大

		resetDigitsR(resultLength);

		if (sign == addend.sign)
		{
			for (int i = 1; i <= loopTime; i++)
			{
				digitsR[i] += digits1[i] + digits2[i];
				digitsR[i + 1] += digitsR[i] / 10;
				digitsR[i] = digitsR[i] % 10;
			}
			resultSign = sign;
		}
		else
		{
			switch (absCompareB(addend))
			{
			case 1:
				for (int i = 1; i <= loopTime; i++)
				{
					if (digits1[i] + digitsR[i] >= digits2[i])
						digitsR[i] += digits1[i] - digits2[i];
					else
					{
						digitsR[i + 1]--;
						digitsR[i] += 10 + digits1[i] - digits2[i];
					}
				}
				resultSign = sign;
				break;
			case 0:
				digitsR[1] = 0;
				resultLength = 1;
				resultSign = 1;
			case -1:
				for (int i = 1; i <= loopTime; i++)
				{
					if (digits2[i] + digitsR[i] >= digits1[i])
						digitsR[i] += digits2[i] - digits1[i];
					else
					{
						digitsR[i + 1]--;
						digitsR[i] += 10 + digits2[i] - digits1[i];
					}
				}
				resultSign = addend.sign;
				break;
			}

		}

		addIndex = calibrateResultLength(resultLength, resultDecimal);//修正錄入位置
		for (int i = resultLength + addIndex; i >= 1 + addIndex; i--)
		{
			resultString += (char)(digitsR[i] + '0');
		}

		return Number(resultSign, resultLength, resultLength - resultDecimal, resultDecimal, resultString);
	}

	Number operator - (const Number& subtractor)
	{
		additionSort(subtractor);

		int resultSign;
		int resultLength;
		int resultDecimal;
		string resultString;

		int addIndex;
		int loopTime;

		resultDecimal = decimal > subtractor.decimal ? decimal : subtractor.decimal;//先取兩者最大
		loopTime = (integer > subtractor.integer ? integer : subtractor.integer) + resultDecimal;//最大小數+最大整數
		resultLength = loopTime + 1;//先取足夠大

		resetDigitsR(resultLength);

		if (sign != subtractor.sign)
		{
			for (int i = 1; i <= loopTime; i++)
			{
				digitsR[i] += digits1[i] + digits2[i];
				digitsR[i + 1] += digitsR[i] / 10;
				digitsR[i] = digitsR[i] % 10;
			}
			resultSign = sign;
		}
		else
		{
			switch (absCompareB(subtractor))
			{
			case 1:
				for (int i = 1; i <= loopTime; i++)
				{
					if (digits1[i] + digitsR[i] >= digits2[i])
						digitsR[i] += digits1[i] - digits2[i];
					else
					{
						digitsR[i + 1]--;
						digitsR[i] += 10 + digits1[i] - digits2[i];
					}
				}
				resultSign = sign;
				break;
			case 0:
				digitsR[1] = 0;
				resultLength = 1;
				resultSign = 1;
				break;
			case -1:
				for (int i = 1; i <= loopTime; i++)
				{
					if (digits2[i] + digitsR[i] >= digits1[i])
						digitsR[i] += digits2[i] - digits1[i];
					else
					{
						digitsR[i + 1]--;
						digitsR[i] += 10 + digits2[i] - digits1[i];
					}
				}
				resultSign = -sign;
				break;
			}
		}

		addIndex = calibrateResultLength(resultLength, resultDecimal);//修正錄入位置
		for (int i = resultLength + addIndex; i >= 1 + addIndex; i--)
		{
			resultString += (char)(digitsR[i] + '0');
		}

		return Number(resultSign, resultLength, resultLength - resultDecimal, resultDecimal, resultString);
	}

	Number operator * (const Number& factor)
	{
		multiplicationSort(factor);

		int resultSign;
		int resultLength;
		int resultDecimal;
		string resultString;

		int addIndex;
		resultDecimal = decimal + factor.decimal;//兩者相加
		resultLength = length + factor.length;//先取足夠大

		resetDigitsR(resultLength);

		for (int secondDigit = 1; secondDigit <= factor.length; secondDigit++)//遍歷到豎式的第二個數
		{
			for (int firstDigit = 1; firstDigit <= length; firstDigit++)//遍歷到豎式的第一個數
			{
				//模擬豎式中的某兩位數相乘
				digitsR[firstDigit + secondDigit - 1] += digits1[firstDigit] * digits2[secondDigit];
				//處理進位
				digitsR[firstDigit + secondDigit] += digitsR[firstDigit + secondDigit - 1] / 10;
				digitsR[firstDigit + secondDigit - 1] = digitsR[firstDigit + secondDigit - 1] % 10;
			}
		}

		resultSign = sign * factor.sign;

		addIndex = calibrateResultLength(resultLength, resultDecimal);//修正錄入位置
		for (int i = resultLength + addIndex; i >= 1 + addIndex; i--)
		{
			resultString += (char)(digitsR[i] + '0');
		}

		return Number(resultSign, resultLength, resultLength - resultDecimal, resultDecimal, resultString);
	}

	Number operator / (const Number& divisor)
	{
		divisionSort(divisor);

		int resultSign;
		int resultLength;
		int resultDecimal;
		string resultString;

		bool canSub;
		int addStartIndex;
		int tempIntLength;

		int checkPointFront = 1;//當前試商時被除數的最前檢查點
		int checkPointTrail = divisor.length;//當前試商時被除數的最後檢查點
		int divisorcheckTrail;//記錄試商時除數的最後檢查點
		//校正純小數數字的檢查點
		if (divisor.length == divisor.decimal)
		{
			for (int i = 0; i < divisor.length; i++)
				if (divisor.numString[i] == '0')
					checkPointTrail--;
				else
					break;
		}
		divisorcheckTrail = checkPointTrail;

		resetDigitsR(sizeof(digitsR) / sizeof(int) - 1);

		for (checkPointTrail; checkPointTrail <= 100; checkPointFront++, checkPointTrail++)
		{
			while (1)
			{
				canSub = true;
				//判斷是否能夠進行減法試商(夠不夠減)
				if (digits1[checkPointFront - 1] == 0)
				{
					//看看夠不夠減
					for (int i = checkPointFront, j = 1; i <= checkPointTrail; i++, j++)
					{
						//過濾掉大於的情況
						if (digits1[i] > digits2[j])
						{
							break;
						}
						//如果執行到這一句,唯一的情況就是前一位等於,且這一位小於,因此整體值爲小於,不能減
						if (digits1[i] < digits2[j])
						{
							canSub = false;
							break;
						}
					}
				}
				if (canSub)
				{
					for (int i = checkPointTrail, j = divisorcheckTrail; j > 0; i--, j--)
					{
						if (digits1[i] >= digits2[j])
							digits1[i] -= digits2[j];
						else
						{
							//否則進行借位減法
							digits1[i - 1]--;
							digits1[i] = 10 + digits1[i] - digits2[j];
						}
					}
					digitsR[checkPointTrail]++;
				}
				else
				{
					break;
				}
			}
		}
		checkPointTrail--;

			tempIntLength = integer + divisor.decimal;
			resultDecimal = checkPointTrail - tempIntLength;
			resultLength = resultDecimal + tempIntLength;

		resultSign = sign * divisor.sign;

		addStartIndex = calibrateResultLengthD(resultLength, resultDecimal);//修正錄入位置
		for (int i = 1 + addStartIndex; i <= resultLength + addStartIndex; i++)
		{
			resultString += (char)(digitsR[i] + '0');
		}

		return Number(resultSign, resultLength, resultLength - resultDecimal, resultDecimal, resultString);
	}

private:
	//用於構造函數中
	void constructNumber(const string& inputString)
	{
		bool decimalCount = false;
		bool frontZero = true;
		int checkLength = length = inputString.length();

		int i = 0;
		//去除正負號
		if (inputString[i] == '-')
		{
			sign = -1;
			length--;
			i++;
		}
		//去除前導0
		for (i; i < checkLength; i++)
		{
			if (inputString[i] == '0')
				length--;
			else
				break;
		}
		//其它數字錄入
		for (i; i < checkLength; i++)
		{
			if (decimalCount)
			{
				decimal++;
			}
			if (inputString[i] == '.')
			{
				decimalCount = true;
				length--;
				//縮短檢測次數來避免錄入小數的末尾0
				for (checkLength; checkLength > i; checkLength--)
				{
					if (inputString[checkLength - 1] == '0')
						length--;
					else
						break;
				}

			}
			else
			{
				numString += inputString[i];
			}
		}
		integer = length - decimal;
	}

	//兩數字絕對值比較
	int absCompareB(const Number& b)
	{
		if (integer > b.integer)
			return 1;
		else if (integer < b.integer)
			return -1;
		else if (numString > b.numString)
			return 1;
		else if (numString == b.numString)
			return 0;
		else 
			return -1;
	}

	//用於加減乘,校準結果數組中的前導0後導0帶來的長度影響,返回刪除的後導0個數
	int calibrateResultLength(int& length, int& decimal)
	{
		int deleteZero = 0;
		//整數部分排除前導0
		for (int i = 0; length > decimal; i++)
		{
			if (digitsR[length] == 0)
				length--;
			else
				break;
		}
		//小數部分排除後導0
		for (int i = 1; decimal != 0; i++)
		{
			if (digitsR[i] == 0)
			{
				decimal--;
				length--;
				deleteZero++;
			}
			else
				break;
		}
		if (0 == length)
		{
			length++;
		}
		return deleteZero;
	}

	//用於除,校準結果數組中的前導0後導0帶來的長度影響,返回刪除的前導0個數
	int calibrateResultLengthD(int& length, int& decimal)
	{
		int deleteZero = 0;
		//小數部分排除後導0
		for (int i = 1; decimal != 0; i++)
		{
			if (digitsR[length] == 0)
			{
				decimal--;
				length--;
			}
			else
				break;
		}
		//整數部分排除前導0
		for (int i = 0; length > decimal; i++)
		{
			if (digitsR[i + 1] == 0)
			{
				length--;
				deleteZero++;
			}
			else
				break;
		}
		if (0 == length)
		{
			length++;
		}
		return deleteZero;
	}

	//把結果數組前n個位數重設爲0
	void resetDigitsR(int length)
	{
		for (int i = 1; i <= length; i++)
		{
			digitsR[i] = 0;
		}
	}

	void additionSort(const Number& otherNumber)
	{
		//i1,i2記錄導入數據的起始位置
		int i1 = 1, i2 = 1;
		if (decimal > otherNumber.decimal)
		{
			i2 += decimal - otherNumber.decimal;
			//第二個數組某些可能用到的元素需要用0賦值
			for (int i = 1; i < i2; i++)
			{
				digits2[i] = 0;
			}
		}
		else if (decimal < otherNumber.decimal)
		{
			i1 += otherNumber.decimal - decimal;
			//第一個數組某些可能用到的元素需要用0賦值
			for (int i = 1; i < i1; i++)
			{
				digits1[i] = 0;
			}
		}
		//對數組重新賦值
		for (int j = 1; j <= length; i1++, j++)
		{
			digits1[i1] = numString[length - j] - '0';
		}
		for (int j = 1; j <= otherNumber.length; i2++, j++)
		{
			digits2[i2] = otherNumber.numString[otherNumber.length - j] - '0';
		}
		for (i1; i1 <= i2; i1++)
		{
			digits1[i1] = 0;
		}
		for (i2; i2 <= i1; i2++)
		{
			digits2[i2] = 0;
		}
	}

	void multiplicationSort(const Number& otherNumber)
	{
		for (int i = 1; i <= length; i++)
		{
			digits1[i] = numString[length - i] - '0';
		}
		for (int i = 1; i <= otherNumber.length; i++)
		{
			digits2[i] = otherNumber.numString[otherNumber.length - i] - '0';
		}
	}

	void divisionSort(const Number& otherNumber)
	{
		int arrayLength = sizeof(digits1) / sizeof(int) - 1;
		for (int i = 1; i <= arrayLength; i++)
		{
			if (i <= length)
				digits1[i] = numString[i - 1] - '0';
			else
				digits1[i] = 0;
		}
		//處理除數
		int j = 1;
		if (otherNumber.length == 0)
		{
			digits2[1] = 1;
		}
		else
		{
			//去除除數的前導0
			for (j; j <= otherNumber.length; j++)
			{
				if (otherNumber.numString[j - 1] != '0')
					break;
			}
		}
		for (int i = 1; j <= otherNumber.length;i++, j++)
		{
			digits2[i] = otherNumber.numString[j - 1] - '0';
		}

	}
};

int Number::digits1[101];
int Number::digits2[101];
int Number::digitsR[101];

//下面函數用於測試:
void numberConversionCheck()
{
	{
		Number pie("3.141592653589793238462643383279502884197169399375105820974944");
		Number radius = 50.2;
		Number height = 10;
		Number Vcone = pie * radius * radius * height / 3;
		cout << Vcone.GetFullNumber() << endl;
	}
}

int main()
{
	numberConversionCheck();
}

這裏再給出2個測試用函數

//字符輸入測試
void textCheck()
{
	while (1)
	{
		char ch[100];
		cout << "請輸入一個數字";
		cin >> ch;
		Number number(ch);
		cout << "numString存儲字符值爲"<< number.numString << endl;
		string copy = number.GetFullNumber();
		cout << "獲取數字函數輸出字符值爲" << copy << endl;
	}
}
//運算測試,可以把倒數第二行的+改成-*/,即可測試其它三個運算
void additionCheck()
{
	while (1)
	{
		char ch1[100];
		char ch2[100];
		cout << endl << "請輸入第一個數字" << endl;
		cin >> ch1;
		cout << endl << "請輸入第二個數字" << endl;
		cin >> ch2;

		Number a(ch1);
		Number b(ch2);
		Number c = a + b;
		cout << c.GetFullNumber() << endl;
	}
}

四、拓展想法

  • BUG修復:其實這個類應該還會有我沒發現的bug
    (因爲字符串與數組的相互轉換帶來了太多細節操作= =)
  • 考慮加入開根等更多運算
    (可以直接用現有的公式,比如牛頓法,都是基於四則運算的)
  • 可以加入字符串的錄入檢測,當輸入不正確的字符串時應該有相應處理
  • 加入Number類到標準類型的轉換,以便必要時數據交互
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章