C++ 基於vector的高精度浮點類

前言

大家好,寫代碼也是一個上癮的事情啊,剛剛結束了赫夫曼編碼我就馬不停蹄的開始着手算數編碼了。不過在我研究了一下算數編碼的原理過後,我發現如果只用C語言默認提供的float和double類型,去完成我們圖像的算數編碼好像有些困難。當我們不斷的劃分區間後,最終的結果將是一個相當精確地小數,而以IEEE754爲標準的double都不能完成這個任務。顯然,我們要搬出曾經的噩夢,高精度算法了。
早在大一學習c++時,我就接觸過高精度算法的一些基礎內容,當時是作爲選做的課後題,我用char*動態分配長度的字符串艱難的完成了+、-、*運算,卡在了除法。這回我先做了一些研究,最後我決定參考這篇博客中提供的代碼,利用vector<char>來比較好的實現一個高精度浮點類。(參考博客中的除法算法不完備,我已改正。另:如有侵權,請告知)

一、類定義

先上代碼:

class WFloat	//高精度浮點數類
{
	//基本運算符重載
	friend WFloat operator+(const WFloat&, const WFloat&);	//加法重載
	friend WFloat operator-(const WFloat&, const WFloat&);	//減法重載
	friend WFloat operator*(const WFloat&, const WFloat&);	//乘法重載
	friend WFloat operator/(const WFloat&, const WFloat&) throw(DividedByZeroException);	//除法重載
	friend WFloat operator-(const WFloat&);	//負號重載
	
	//比較重載
	friend bool operator==(const WFloat&, const WFloat&);	//等於重載
	friend bool operator!=(const WFloat&, const WFloat&);	//不等於重載
	friend bool operator<(const WFloat&, const WFloat&);	//小於重載
	friend bool operator<=(const WFloat&, const WFloat&);	//小於等於重載
	friend bool operator>(const WFloat&, const WFloat&);	//大於重載
	friend bool operator>=(const WFloat&, const WFloat&);	//大於等於重載

	//擴展運算符重載
	friend WFloat operator+=(WFloat&, const WFloat&);	//加等重載
	friend WFloat operator-=(WFloat&, const WFloat&);	//減等重載
	friend WFloat operator*=(WFloat&, const WFloat&);	//乘等重載
	friend WFloat operator/=(WFloat&, const WFloat&);	//除等重載
	
	//輸入輸出重載
	friend ostream& operator<<(ostream&, const WFloat&);	//輸出重載
	friend istream& operator>>(istream&, WFloat&);	//輸入重載

public:
	WFloat();
	WFloat(int);	//用一個整數構造
	WFloat(string&);	//用一個字符串構造
	WFloat(const WFloat&);	//用一個高精度數構造
	WFloat operator=(const WFloat&);	//賦值函數
	WFloat abs() const;	//取絕對值
	~WFloat() {}

	static const WFloat ZERO;	//定義0
	static const WFloat ONE;	//定義1
	static const WFloat TEN;	//定義10(用於除法化整)

private:
	vector<char>integer;	//整數部分
	vector<char>decimal;	//小數部分
	void trim();	//將多餘的零刪去
	bool tag;	//用來表示正負,true爲正
};

1、私有成員

首先看私有成員,我們用兩個vector<char>類型的動態數組分別存儲浮點數的整數部分和小數部分,我們在讀入一個高精度數時(比如用字符串來構造),按照從後(右)往前(左)的順序分別寫入到小數部分和整數部分,以'.'小數點作爲分隔。例如:輸入“1234.5678”,那麼小數部分的存儲如下8 7 6 5,整數部分爲4 3 2 1
正因爲我們按照上述的方式存儲,因此在小數部分的首部可能會出現多餘的0,例如輸入"1.5600",那麼小數部分就是0 0 6 5,同理可知整數部分的尾部可能會有多餘的0。爲了解決這個問題,提供了函數trim()來刪除多餘的0。
對於正負的表示,我們用了一個布爾型的tag標籤,這主要是便於條件判斷。

2、公有成員

公有成員主要是提供了構造函數,可以看到我們可以使用int、string、WFloat來構造一個高精度浮點數,其中利用string和WFloat構造的情況比較多,int型只是爲了表示高精度整型。
我們還定義了三個靜態變量,分別是0、1、10,其用處會在運算符重載部分看到。

3、友元函數

友元函數佔了最大頭,但是沒什麼好說的,我們的目的就是重載所有的相關運算符,來實現一個可以與double、float近似的高精度浮點類。由於我的需求是完成算數編碼,因此我只實現了基礎四則運算,布爾運算,如果需要平方、開方等運算,可以自行實現。

二、重要函數實現

因爲按照慣例我會在文末給出倉庫傳送門,所以全部的代碼大家都可以查到。出於篇幅的考慮,我決定只將最重要的函數講解一下(實際上也不少了)

1、構造函數

構造函數我們只講用string類型構造

WFloat::WFloat(string &num)	//用字符串初始化,格式形如"-123.456"、"1.0"
{
	bool type = true;	//用於判斷小數部分是否結束
	tag = true;	//默認爲正數,讀到'-'再變爲負數
	for (string::reverse_iterator iter = num.rbegin(); iter < num.rend(); iter++)	  //逆向迭代
	{
		char ch = (*iter);
		if (ch == '.')	//遇到小數點則開始向整數部分寫入
		{
			type = false;
			iter++;
		}
		if (iter == num.rend() - 1)	//讀取正負號
		{
			if (ch == '+')
			{
				break;
			}
			if (ch == '-')
			{
				tag = false;
				break;
			}
		}
		//利用逆向迭代器,將整個數據倒序存入
		if (type)
			decimal.push_back((char)((*iter) - '0'));
		else
			integer.push_back((char)((*iter) - '0'));
	}
}

就像我在類定義裏所說的,我們用一個逆向迭代器從後往前的賦值,在遇到小數點之前將字符存入小數部分,之後存入整數部分。

2、trim()

整理函數拿出來說是因爲參考博客中寫的有一點問題。

void WFloat::trim()	
{
	//因爲我們是逆向存儲的,所以整數的尾部和小數的首部可能會有多餘的0
	vector<char>::reverse_iterator iter = integer.rbegin();	//對整數部分
	while (!integer.empty() && (*iter) == 0)
	{
		integer.pop_back();	//指向不爲空且尾部爲0,刪去
		iter = integer.rbegin();	//再次指向尾部
		//整數部分的“尾部”就是最高位,如00515.424900的左兩個0
	}

	if (integer.size() == 0 && decimal.size() == 0)	//如果整數、小數全爲空
	{
		tag = true;
	}

	if (integer.size() == 0)	//如果整數部分是0
	{
		integer.push_back(0);
	}

	vector<char>::const_iterator it = decimal.begin();	//對小數部分
	while (!decimal.empty() && (*it) == 0)
	{
		it = decimal.erase(it);	//指向不爲空且首部爲0,刪去
		//小數部分的“首部”就是最低位,上例中的右兩個0
	}

	if (decimal.size() == 0)	//如果小數部分是0
	{
		decimal.push_back(0);
	}
}

參考博客中對於符號的修改有問題,應該是當整數部分和小數部分同時爲0時,纔將符號修改爲正,而不是任一爲0時修改爲正。這個問題會影響後面乘法與除法的結果符號,特此說明。

3、輸入與輸出

3.1 operator<<
ostream& operator<<(ostream& out, const WFloat& num)	//輸出重載
{
	if (!num.tag)	//負數
	{
		out << "-";
	}

	for (vector<char>::const_reverse_iterator iter = num.integer.rbegin(); iter != num.integer.rend(); iter++)  //輸出整數部分
	{
		out << (char)((*iter) + '0');
	}

	cout << '.';

	for (vector<char>::const_reverse_iterator iter = num.decimal.rbegin(); iter != num.decimal.rend(); iter++)  //輸出小數部分
	{
		out << (char)((*iter) + '0');
	}
	return out;
}

輸出部分只要注意從後往前的順序即可。

3.2 operator>>
istream& operator>>(istream& in, WFloat& num)	//輸入重載
{
	string str;
	in >> str;
	num = WFloat(str);
	return in;
}

輸入部分我們調用構造函數。

4、布爾判斷

我們先寫布爾判斷部分,因爲在計算過程中需要使用到兩個高精度數之間的各種判斷。

4.1 operator<

小於和等於,通過一些簡單的布爾代數運算就可以推導出全部邏輯關係,因此我們只列出小於和等於的定義。

bool operator<(const WFloat& num1, const WFloat& num2)	//小於重載
{
	bool sign;	//返回值
	if (num1.tag != num2.tag)	//如果異號
	{
		sign = !num1.tag;	//如果num1正,則不小於;反之,則小於
		return sign;
	}
	else
	{
		//如果同號,先比較整數再比較小數
		if (num1.integer.size() != num2.integer.size())	//如果整數部分不等長
		{
			if (num1.tag)	//如果同爲正,則整數部分長的大
			{
				sign = num1.integer.size() < num2.integer.size();	
				return sign;
			}
			else
			{
				//同爲負,則整數部分長的小
				sign = num1.integer.size() > num2.integer.size();
				return sign;
			}
		}
		//如果整數部分等長
		vector<char>::const_reverse_iterator iter1, iter2;
		iter1 = num1.integer.rbegin();
		iter2 = num2.integer.rbegin();
		while (iter1 != num1.integer.rend())
		{
			if (num1.tag && *iter1 < *iter2)
				return true;
			if (num1.tag && *iter1 > *iter2)
				return false;
			if (!num1.tag && *iter1 > *iter2)
				return true;
			if (!num1.tag && *iter1 < *iter2)
				return false;
			iter1++;
			iter2++;
		}

		//下面比較小數部分
		vector<char>::const_reverse_iterator it1, it2;
		it1 = num1.decimal.rbegin();
		it2 = num2.decimal.rbegin();
		while (it1 != num1.decimal.rend() && it2!=num2.decimal.rend())
		{
			if (num1.tag && *it1 < *it2)
				return true;
			if (num1.tag && *it1 > *it2)
				return false;
			if (!num1.tag && *it1 > *it2)
				return true;
			if (!num1.tag && *it1 < *it2)
				return false;
			it1++;
			it2++;
		}
		//如果整數部分,而小數部分停止前全部一樣,那麼看誰的小數位更多
		return(num1.tag && it2 != num2.decimal.rend()) || (!num1.tag && it1 != num1.decimal.rend());
	}
}

布爾運算這部分主要是情況的考慮完全問題。異號的情況最容易判斷,正數爲大,因此我們將同號、異號作爲最外層的條件判斷。
當同號時,先比較整數部分、再比較小數部分。整數部分比較時先比較長度,如果沒有結果再逐位比較;整數部分完全相同時,需要逐位比較小數部分。

4.1 operator==

等於和小於的判斷邏輯幾乎相同,不再複述。

bool operator==(const WFloat& num1, const WFloat& num2)	//等於重載
{
	if (num1.tag != num2.tag)
		return false;
	if (num1.integer.size() != num2.integer.size())
		return false;
	if (num1.decimal.size() != num2.decimal.size())
		return false;

	//如果長度和符號相同,那麼下面逐位比較
	vector<char>::const_iterator iter1, iter2;
	iter1 = num1.decimal.begin();
	iter2 = num2.decimal.begin();
	while (iter1 != num1.decimal.end())
	{
		if (*iter1 != *iter2)
			return false;
		iter1++;
		iter2++;
	}

	iter1 = num1.integer.begin();
	iter2 = num2.integer.begin();
	while (iter1 != num1.integer.end())
	{
		if (*iter1 != *iter2)
			return false;
		iter1++;
		iter2++;
	}
	return true;
}

5、運算符重載

+、-、*、/可以轉化爲+=、-=、*=、/=,因此我們只需要完成後者。

5.1 operator+=

加、減運算是相對容易完成的部分。我們先上代碼,再對其中的部分進行精解。

WFloat operator+=(WFloat& num1, const WFloat& num2)	//加等於重載
{
	if (num1.tag == num2.tag)	//只處理同符號數,異號由-減法處理
	{
		vector<char>::iterator iter1;
		vector<char>::const_iterator iter2, it;

		//先處理小數部分
		int num1_decimal_size = num1.decimal.size();	//小數部分長度
		int num2_decimal_size = num2.decimal.size();
		char carry = 0;	//進位
		if (num1_decimal_size < num2_decimal_size)	//如果num2小數部分更長
		{
			iter1 = num1.decimal.begin();
			iter2 = num2.decimal.begin();
			iter2 = iter2 - (num1_decimal_size - num2_decimal_size);	//將指向調整到一一對應的位置

			while (iter1 != num1.decimal.end() && iter2 != num2.decimal.end())
			{
				(*iter1) = (*iter1) + (*iter2) + carry;
				carry = ((*iter1) > 9);	//如果大於9則carry=1
				(*iter1) = (*iter1) % 10;
				iter1++;
				iter2++;
			}

			it = num2.decimal.begin();
			iter2 = num2.decimal.end();
			iter2 = iter2 - num1_decimal_size - 1;	//指向長出部分
			while (iter2 != it)
			{
				num1.decimal.insert(num1.decimal.begin(), *iter2);
				iter2--;
			}
			num1.decimal.insert(num1.decimal.begin(), *iter2);
			iter1 = num1.decimal.begin();
		}
		else
			if (num1_decimal_size > num2_decimal_size) //如果num1小數部分更長,同理
			{
				iter1 = num1.decimal.begin();
				iter1 = iter1 + (num1_decimal_size - num2_decimal_size);
				//將指向調整到一一對應的位置
				iter2 = num2.decimal.begin();

				while (iter1 != num1.decimal.end() && iter2 != num2.decimal.end())
				{
					(*iter1) = (*iter1) + (*iter2) + carry;
					carry = ((*iter1) > 9);	//如果大於9則carry=1
					(*iter1) = (*iter1) % 10;
					iter1++;
					iter2++;
				}
			}
			else
			{
				iter1 = num1.decimal.begin();	//如果二者等長
				iter2 = num2.decimal.begin();
				while (iter1 != num1.decimal.end() && iter2 != num2.decimal.end())
				{
					(*iter1) = (*iter1) + (*iter2) + carry;
					carry = ((*iter1) > 9);	//如果大於9則carry=1
					(*iter1) = (*iter1) % 10;
					iter1++;
					iter2++;
				}
			}

		//再處理整數部分
		iter1 = num1.integer.begin();
		iter2 = num2.integer.begin();
		//從個位開始相加
		while (iter1 != num1.integer.end() && iter2 != num2.integer.end())
		{
			(*iter1) = (*iter1) + (*iter2) + carry;
			carry = ((*iter1) > 9);	//如果大於9則carry=1
			(*iter1) = (*iter1) % 10;
			iter1++;
			iter2++;
		}
		//總會有一個先到達end()
		while (iter1 != num1.integer.end())	//如果被加數更長,處理進位
		{
			(*iter1) = (*iter1) + carry;
			carry = ((*iter1) > 9);	//如果大於9則carry=1
			(*iter1) = (*iter1) % 10;
			iter1++;
		}
		while (iter2 != num2.integer.end())	//加數更長
		{
			char val = (*iter2) + carry;
			carry = (val > 9);
			val %= 10;
			num1.integer.push_back(val);
			iter2++;
		}
		if (carry != 0)	//如果還有進位,則說明要添加一位
		{
			num1.integer.push_back(carry);
		}
		return num1;
	}
	else
	{	//如果異號
		if (num1.tag)	//如果被加數爲正,加數爲負,相當於減等於
		{
			WFloat temp(-num2);
			return num1 -= temp;
		}
		else
		{
			WFloat temp(-num1);
			return num1 = num2 - temp;
		}
	}
}

可以看到,異號時的加法相當於減法,因此我們的+=定義中只處理同號情況,異號交由-處理。
我們的運算操作幾乎都是模仿人們的手算進行的,因此就像草紙演算,我們從後往前進行逐位運算。
在計算時首先應當將位數對其,這主要是針對小數部分。運算由三部分組成,當前位被加數、加數、進位(這有點像用代碼實現一個加法器),注意對被加數進行模10來保證取值出於0-9之間。
總體來說加減法沒有理解難度,並且我的註釋很詳細,應該不難掌握。

5.2 operator-=

同理加法,我們只處理同號的情況,異號交由+處理。

WFloat operator-=(WFloat& num1, const WFloat& num2)	//減等於重載
{
 	if (num1.tag == num2.tag)	//只處理同號,異號由+加法處理
	{
		if (num1.tag)	//如果同爲正
		{
			if (num1 < num2)	//且被減數小
			{
				WFloat temp(num2 - num1);
				num1 = -temp;
				return num1;
			}
		}
		else
		{
			if (-num1 > -num2)	//如果同爲負,且被減數絕對值大
				return num1 = -((-num1) - (-num2));
			else
				return num1 = (-num2) - (-num1);
		}

		//下面是同爲正,且減數小的情況
		//小數部分 
		char borrow = 0;  //借位
		int num1_decimal_size = num1.decimal.size();
		int num2_decimal_size = num2.decimal.size();
		vector<char>::iterator it1 = num1.decimal.begin();
		vector<char>::const_iterator it2 = num2.decimal.begin();

		if (num1_decimal_size > num2_decimal_size)	//如果被減數小數部分更長
		{
			num1_decimal_size -= num2_decimal_size;	//長出部分
			it1 = it1 + num1_decimal_size;	//跳過長出部分
		}
		else 
		{	//如果減數的小數部分更長,則需要給被減數補0
			int number = num2_decimal_size - num1_decimal_size;
			while (number != 0)
			{
				num1.decimal.insert(num1.decimal.begin(), 0); //缺少的位數補0
				number--;
			}
			it1 = num1.decimal.begin();	//插入後需要重新指向
			it2 = num2.decimal.begin();
		}
		while ((it1 != num1.decimal.end()) && (it2 != num2.decimal.end()))
		{
			(*it1) = (*it1) - (*it2) - borrow;
			borrow = 0;
			if ((*it1) < 0)
			{
				borrow = 1;
				(*it1) += 10;
			}
			it1++;
			it2++;
		}
		//整數部分
		vector<char>::iterator iter1;
		vector<char>::const_iterator iter2;
		iter1 = num1.integer.begin();
		iter2 = num2.integer.begin();

		while (iter1 != num1.integer.end() && iter2 != num2.integer.end())
		{
			(*iter1) = (*iter1) - (*iter2) - borrow;
			borrow = 0;
			if ((*iter1) < 0) {
				borrow = 1;
				(*iter1) += 10;
			}
			iter1++;
			iter2++;
		}
		while (iter1 != num1.integer.end()) {
			(*iter1) = (*iter1) - borrow;
			borrow = 0;
			if ((*iter1) < 0) 
			{
				borrow = 1;
				(*iter1) += 10;
			}
			else break;
			iter1++;
		}
		num1.trim();	//把多餘的0去掉
		return num1;
	}
	else 
	{
		//如果異號
		if (num1 > WFloat::ZERO)
		{
			WFloat temp(-num2);
			return num1 += temp;
		}
		else
		{ 
			WFloat temp(-num1);
			return num1 = -(num2 + temp);
		}
	}
}

減法需要考慮結果的符號問題,最好的解決辦法就是將問題儘可能轉化成同一類。在最初的一系列條件判斷後,我們只需要處理同爲正且減數小於被減數的情況。
注意,在小數部分,給被減數補0的操作結束後,一定要重新將迭代器指向decimal.begin()。參考博客中沒有這兩行代碼,我在運行時會報錯。
剩下的運算部分與加法大同小異,不再複述。

5.3 operator*=

乘除部分的難度就提高了,尤其是除法還要更加複雜一些。我們先看乘法

WFloat operator*=(WFloat& num1, const WFloat& num2)	//乘等於重載
{
	WFloat result(0);	//儲存結果
	if (num1 == WFloat::ZERO || num2 == WFloat::ZERO)	//有0做乘數得0
		result = WFloat::ZERO;
	else
	{
		int size = 0;
		vector<char>temp_num1(num1.integer.begin(), num1.integer.end());	//一個臨時變量,用於將整數部分與小數部分合並
		if (num1.decimal.size() != 1 || (num1.decimal.size() == 1 && (*num1.decimal.begin()) != 0)) //如果被乘數有小數部分,插入小數
		{
			temp_num1.insert(temp_num1.begin(), num1.decimal.begin(), num1.decimal.end());
			size += num1.decimal.size();
		}
		
		vector<char>temp_num2(num2.integer.begin(), num2.integer.end());	//一個臨時變量,用於將整數部分與小數部分合並
		if (num2.decimal.size() != 1 || (num2.decimal.size() == 1 && (*num2.decimal.begin()) != 0)) //如果被乘數有小數部分,插入小數
		{
			temp_num2.insert(temp_num2.begin(), num2.decimal.begin(), num2.decimal.end());
			size += num2.decimal.size();
		}

		//開始乘法
		vector<char>::const_iterator iter2 = temp_num2.begin();
		while (iter2 != temp_num2.end())
		{
			if (*iter2 != 0)
			{
				deque<char>temp(temp_num1.begin(), temp_num1.end());
				char carry = 0;	//進位
				deque<char>::iterator iter1 = temp.begin();
				while (iter1 != temp.end())	//被乘數乘以某一位乘數
				{
					(*iter1) *= (*iter2);
					(*iter1) += carry;
					carry = (*iter1) / 10;
					(*iter1) %= 10;
					iter1++;
				}
				if (carry != 0)
				{
					temp.push_back(carry);
				}
				int num_of_zeros = iter2 - temp_num2.begin();	//計算錯位
				while (num_of_zeros--) 
					temp.push_front(0);	//乘得結果後面添0
				WFloat temp2;
				temp2.integer.clear();
				temp2.integer.insert(temp2.integer.end(), temp.begin(), temp.end());
				temp2.trim();
				result = result + temp2;
			}
			iter2++;
		}
		result.tag = ((num1.tag && num2.tag) || (!num1.tag && !num2.tag));

		//由於我們將小數和整數合併在一起,因此下面要把小數點重新添上
		if (size != 0)
		{
			if (size >= result.integer.size())	//說明需要補前導0
			{
				int n = size-result.integer.size();
				for (int i = 0; i <= n; i++)
					result.integer.insert(result.integer.end(), 0);
			}
			result.decimal.clear();
			result.decimal.insert(result.decimal.begin(), result.integer.begin(), result.integer.begin() + size);
			result.integer.erase(result.integer.begin(), result.integer.begin() + size);
		}
	}
	num1 = result;
	num1.trim();
	return num1;
}

這裏我們開始用到最初定義的靜態變量的,如果有乘數爲0,那麼直接返回0。
否則我們開始運算。小數整數混雜着運算很複雜,因此我們將二者歸併到一起,在乘法的運算過程中相當於使用一個大整數進行,最後再補上小數點即可。
運算的過程容易理解,但是在最後拆分小數和整數時,參考博客的代碼是有問題的。考慮乘法運算0.545*0.102,最後的結果是0.0xxx,這會出現一個問題,那就是我們記錄的小數點所在位置size,要大於我們運算結果的長度。這是因爲我們需要補充前導0,才能正確的區分整數和小數部分。因此可以看到我再最後添加了一個補充前導0的判斷語句。

5.4 operator/=

這部分參考博客的少考慮了一些情況,並且計算結果的小數部分錯誤的多了一個0,經過我的修正暫時沒有發現其他問題。

WFloat operator/=(WFloat& num1, const WFloat& num2)	//除等於重載
{
	if (num2 == WFloat::ZERO)
		throw DividedByZeroException();
	if (num1 == WFloat::ZERO)
		return num1;

	WFloat temp_num1 = num1;
	WFloat temp_num2 = num2;
	if (temp_num1.tag == false || temp_num2.tag == false)	//轉換成無符號除法來做
	{
		temp_num1.tag = true;
		temp_num2.tag = true;
	}
	
	int Integer_Size = 0;	//整數部分應爲幾位
	if ((temp_num2.decimal.size() == 1) && (*(temp_num2.decimal.begin()) == 0)) {} //如果除數沒有小數部分,不做操作
	else
	{
		//否則把除數和乘數同時擴大,直到除數爲整數(只對Integer部分運算)
		int t = temp_num2.decimal.size();
		while (t--)
		{
			temp_num1 = temp_num1 * WFloat::TEN;
			temp_num2 = temp_num2 * WFloat::TEN;
		}
	}
	if (temp_num1 < temp_num2)	//被除數小於除數,應該是0.xxx
	{
		while (temp_num1 < temp_num2)
		{
			temp_num1 *= WFloat::TEN;
			Integer_Size--;
		}
	}
	else
	{
		while (temp_num1 > temp_num2)
		{
			temp_num1.decimal.push_back(*temp_num1.integer.begin());
			temp_num1.integer.erase(temp_num1.integer.begin());
			Integer_Size++;
		}
	}

	int k = ACCURACY;
	WFloat quotient(0);	//商

	while (k--)
	{
		if (temp_num1 < temp_num2)
		{
			temp_num1 = temp_num1 * WFloat::TEN;
			quotient = quotient * WFloat::TEN;
		}
		else
		{
			int i;
			WFloat compare;
			for (i = 1; i <= 10; i++)	//“試商”
			{
				WFloat BF(i);
				compare = temp_num2 * BF;
				if (compare > temp_num1)
					break;
			}
			compare -= temp_num2;
			temp_num1 -= compare;
			WFloat index(i - 1);
			quotient = quotient + index;
		}
	}

	if (Integer_Size < 0)	//如果是小數除以大數,結果爲0.xxx
	{
		vector<char> temp(quotient.integer.begin(), quotient.integer.end());
		quotient.integer.clear();
		quotient.integer.push_back(0);	//整數部分爲0

		quotient.decimal.clear();
		int count_zero = -Integer_Size;
		//下面先補充前導0
		while (--count_zero)
		{
			quotient.decimal.insert(quotient.decimal.begin(), 0);
		}
		quotient.decimal.insert(quotient.decimal.begin(), temp.begin(), temp.end());
	}
	else
	{
		if (quotient.integer.size() > Integer_Size)
		{
			vector<char> temp(quotient.integer.begin(), quotient.integer.end());

			quotient.integer.clear();	//這裏如果不清空會有錯誤

			quotient.integer.assign(temp.end() - Integer_Size, temp.end());

			quotient.decimal.clear();	//同理需要清空

			quotient.decimal.insert(quotient.decimal.begin(), temp.begin(), temp.end() - Integer_Size);
		}
		else
		{
			//這一部分意義不明,我覺得不會走到這個分支
			int t = Integer_Size - quotient.integer.size();
			while (t--)
			{
				quotient = quotient * WFloat::TEN;
			}
		}
	}
	quotient.tag = ((num1.tag && num2.tag) || (!num1.tag && !num2.tag));
	num1 = quotient;
	num1.trim();
	return num1;
}

說一下參考博客中的問題:
首先是“試商”的部分,商的結果可以是0-9,參考博客中的寫法無法取到0和9,這會導致運算結果的不正確。
另外就是沒有考慮到小數除以大數的情況,Integer_Size爲負數時說明結果爲0.xxx,我們應該補充上前導0才能得到正確的結果。
最後,雖然vector提供的assign函數理論上會清空原始數據,但是我實測,如果不手動clear掉原始數據,那麼assign之後會報錯。
除法的精度由全局變量ACCURACY決定,這裏設置的是100,如果真的要用於算數編碼,肯定還是不夠的。

總結

以無頭蒼蠅四處亂撞的方式去寫代碼總是會走無數的岔路,這次按照參考博客中的思路一步一步來,不斷的完善它的錯誤,也是一種很好的鍛鍊。所以說,你沒必要強迫自己寫每一行代碼,但你應該讀懂自己寫下的每一行代碼。
本次高精度浮點數內容的具體定義與實現代碼可見我的giteeWFloat.h頭文件,我也提供了測試用的主函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章