高精度浮點類
前言
大家好,寫代碼也是一個上癮的事情啊,剛剛結束了赫夫曼編碼我就馬不停蹄的開始着手算數編碼了。不過在我研究了一下算數編碼的原理過後,我發現如果只用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,如果真的要用於算數編碼,肯定還是不夠的。
總結
以無頭蒼蠅四處亂撞的方式去寫代碼總是會走無數的岔路,這次按照參考博客中的思路一步一步來,不斷的完善它的錯誤,也是一種很好的鍛鍊。所以說,你沒必要強迫自己寫每一行代碼,但你應該讀懂自己寫下的每一行代碼。
本次高精度浮點數內容的具體定義與實現代碼可見我的gitee,WFloat.h
頭文件,我也提供了測試用的主函數。