C++中如何處理超長的數(shù)字(long long類型的整數(shù)都無法存儲(chǔ)的)
在 C++中,如果數(shù)字超出了 long long 類型的范圍,可以考慮使用字符串或第三方庫(如 Boost.Multiprecision)來表示和處理超長數(shù)字。要使用第三方庫需要下載并安裝所需的第三方庫,在此就不介紹了。
在此介紹使用字符串表示和處理超長數(shù)字。本文將介紹使用C++字符串實(shí)現(xiàn)超長的數(shù)字加法、減法、乘法和除法運(yùn)算。
1.判斷一個(gè)非負(fù)的超大數(shù)是奇數(shù)還是偶數(shù),源碼如下:
#include <iostream>
#include <string>
using namespace std;
int main() {
//string s = "123456789123456789123456789";
cout << "請(qǐng)輸入一個(gè)正整數(shù):" << endl;
cin >> s;
// s.size()是字符串的長度,即由多少字符組成的, 字符串的最后一位字符即s[s.size() - 1]
char c = s[s.size() - 1];
cout << "最后一位數(shù)字是" << c << endl;
// 將一個(gè)整數(shù)數(shù)字字符變成整數(shù),我們只需要將它減字符'0'
int i = c - '0';
if (i % 2 == 0) {
cout << s << "是偶數(shù)";
} else {
cout << s << "是奇數(shù)";
}
return 0;
}
2.判斷兩個(gè)超大正整數(shù)的大小
#include <iostream>
#include <string>
using namespace std;
bool Lower(string str1, string str2){
//長度長的一定大(這里假設(shè)除了0以外,都沒有前導(dǎo)0),長度相等則字典序小的數(shù)字更小
return str1.size()<str2.size()||(str1.size()==str2.size()&&str1<str2);
}
int main() {
cout << "請(qǐng)輸入兩個(gè)超大正整數(shù):" << endl;
string a, b;
cin >> a >> b;
if (Lower(a, b)){
cout << a << "小于" << b << endl;
}
else{
cout << a << "大于" << b << endl;
}
return 0;
}
下面分別介紹加法、減法、乘法和除法運(yùn)算的實(shí)現(xiàn)方法。特別說明,加減運(yùn)算僅限非負(fù)整數(shù)的運(yùn)算,因?yàn)樨?fù)數(shù)的加減運(yùn)算可以等價(jià)為某種形式的加法或者減法運(yùn)算,故不做考慮;乘除運(yùn)算只考慮大整數(shù)運(yùn)算,不考慮小數(shù)的計(jì)算;除法只考慮大整數(shù)運(yùn)算,計(jì)算結(jié)果精確到6位小數(shù)(6位小數(shù)后直接舍去)。
參考https://blog.songjiahao.com/archives/382
3. 非負(fù)大整數(shù)的加法
大數(shù)加法的實(shí)現(xiàn),模仿了我們列豎式的計(jì)算方法,從個(gè)位開始,一位一位的相加,每次考慮進(jìn)位即可。由于讀入的字符串0號(hào)位置為最高位,所以我們采用逆序的辦法訪問字符串,然后每位計(jì)算即可。同樣的,因?yàn)橛?jì)算時(shí)候得到的每一位都是逆序的,最后的結(jié)果要進(jìn)行逆置。
加法,可能導(dǎo)致最終位數(shù)多一位(最高位的進(jìn)位),所以要記得處理。
最后,我們來考慮一些特殊情況,比如兩個(gè)數(shù)字都是0,或者其中有一個(gè)是0,我們就可以快速得到結(jié)果,省去了遍歷過程。
實(shí)現(xiàn)代碼
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
//判斷是否為0,全部為0的數(shù)字就是0,比如00000
bool CheckZero(const string &str){ //檢查是否等于0
int size=str.size(); //如果全是0則為0,這里假設(shè)不會(huì)有帶符號(hào)的+00000或者-00000作為輸入
for(int i=0;i<size;i++)
if(str[i]!='0') return false;
return true;
}
string Add(string str1, string str2){
//關(guān)于0的處理
if(CheckZero(str1)&&CheckZero(str2)) return "0"; //如果都是0
else if(CheckZero(str1)) return str2; //如果有個(gè)一為0
else if(CheckZero(str2)) return str1;
string ans;
int i=str1.size()-1,j=str2.size()-1;
int flag=0; //進(jìn)位標(biāo)記
while(i>=0||j>=0||flag){
int numa=i>=0?str1[i--]-'0':0; //如果所有位都訪問完畢,對(duì)應(yīng)的位置用0代替
int numb=j>=0?str2[j--]-'0':0;
flag=numa+numb+flag; //計(jì)算兩位的和
ans+='0'+flag%10; //取個(gè)位保存在答案中
flag/=10; //計(jì)算進(jìn)位
}
reverse(ans.begin(),ans.end());
return ans;
}
int main(){
cout << "請(qǐng)輸入兩個(gè)正整數(shù):" << endl;
string a, b;
cin >> a >> b;
string result = Add(a, b);
cout << "和:" << result << endl;
return 0;
}
4. 非負(fù)大整數(shù)的減法
大數(shù)減法的實(shí)現(xiàn),也模仿了我們列豎式的計(jì)算方法。從個(gè)位起,每次計(jì)算一位,首先根據(jù)后一位是否借位,先減去借位,然后判斷當(dāng)前位是否夠減,如果需要借位,則向前一位借位后在減,直到運(yùn)算完畢。同樣的,因?yàn)槲覀円獜膫€(gè)位開始計(jì)算,所以計(jì)算得到的結(jié)果必然是逆序的,最終要記得將結(jié)果逆置。
減法,可能出現(xiàn)前導(dǎo)0,要記得清除前導(dǎo)零。
最后,我們來考慮一些特殊情況,比如兩個(gè)數(shù)相同或者有一個(gè)數(shù)字為0,我們可以直接得到結(jié)果,從而避免了復(fù)雜的處理過程。
實(shí)現(xiàn)代碼
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
//判斷兩個(gè)超大正整數(shù)的大小
bool Lower(string str1, string str2){
//長度長的一定大(這里假設(shè)除了0以外,都沒有前導(dǎo)0),長度相等則字典序小的數(shù)字更小
return str1.size()<str2.size()||(str1.size()==str2.size()&&str1<str2);
}
//判斷是否為0,全部為0的數(shù)字就是0,比如00000
bool CheckZero(const string &str){ //檢查是否等于0
int size=str.size(); //如果全是0則為0,這里假設(shè)不會(huì)有帶符號(hào)的+00000或者-00000作為輸入
for(int i=0;i<size;i++)
if(str[i]!='0') return false;
return true;
}
string Sub(string str1, string str2){
//處理0的情況
if(str1==str2||(CheckZero(str1)&&CheckZero(str2))) return "0"; //如果兩數(shù)相等或者都是0
else if(CheckZero(str1)) return "-"+str2; //如果第一個(gè)數(shù)字為0
else if(CheckZero(str2)) return str1; //如果第二個(gè)數(shù)字為0
//定正負(fù)
int negative=0; //結(jié)果的正負(fù)號(hào)
if(Lower(str1,str2)){
swap(str1,str2); //保證str1大于str2
negative=1; //如果str1小于str2,則結(jié)果過為負(fù)值
}
string ans;
int i=str1.size()-1,j=str2.size()-1;//逆序開始處理
int flag=0; //借位標(biāo)記
while(i>=0||j>=0){
int numa=i>=0?str1[i--]-'0':0; //取每一位,因?yàn)殚L度可能不同所以當(dāng)某一個(gè)已經(jīng)讀取完畢時(shí)對(duì)應(yīng)位置取0
int numb=j>=0?str2[j--]-'0':0;
numa-=flag; //先減去借位
if(numa<numb){ //如果不夠減則向上一位借位(只可能借一位)
numa+=10; //借位并記錄借位
flag=1;
}
else flag=0; //如果不借位,則借位標(biāo)記為0
ans+='0'+numa-numb; //計(jì)算當(dāng)前位置并保存
}
i=ans.size()-1;
while(ans[i]=='0') i--;
ans=ans.substr(0,i+1); //去除前導(dǎo)0,如111-110=1
if(negative) ans+='-'; //如果計(jì)算結(jié)果是負(fù)數(shù),添加負(fù)數(shù)符號(hào)
reverse(ans.begin(),ans.end()); //因?yàn)槭悄嫘蛴?jì)算得到的結(jié)果,所以需要翻轉(zhuǎn)一下
return ans;
}
int main(){
cout << "請(qǐng)輸入兩個(gè)正整數(shù):" << endl;
string a, b;
cin >> a >> b;
string result = Sub(a, b);
cout << "差: " << result << endl;
return 0;
}
5.大整數(shù)的乘法
大數(shù)乘法的實(shí)現(xiàn),還采用我們豎式計(jì)算的方法。從個(gè)位開始,每次計(jì)算被乘數(shù)和乘數(shù)一位的積,然后借助我們寫好的大數(shù)加法實(shí)現(xiàn)最終結(jié)果的累加。但是大數(shù)乘法需要考慮正負(fù)的問題,所以需要對(duì)正負(fù)號(hào)進(jìn)行處理,對(duì)兩個(gè)數(shù)的符號(hào)使用異或最終可以確定乘積結(jié)果的正負(fù)。
乘法,因?yàn)槌藬?shù)的每一位都有相應(yīng)的權(quán)值(個(gè)十百千萬),因此我們對(duì)于乘數(shù)每一位的積進(jìn)行運(yùn)算時(shí)要考慮該位置的權(quán)值,在積的后邊補(bǔ)充相應(yīng)個(gè)數(shù)的零即可。
最后,我們考慮一些特殊情況,比如兩個(gè)數(shù)字中只要有一個(gè)是0,則結(jié)果就是0。
實(shí)現(xiàn)代碼
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
//判斷兩個(gè)超大正整數(shù)的大小
bool Lower(string str1, string str2){
//長度長的一定大(這里假設(shè)除了0以外,都沒有前導(dǎo)0),長度相等則字典序小的數(shù)字更小
return str1.size()<str2.size()||(str1.size()==str2.size()&&str1<str2);
}
//判斷是否為負(fù),只需要判斷第一位是不是負(fù)號(hào)即可,這里不考慮正號(hào)的存在,即認(rèn)為不使用正號(hào)
bool CheckNegative(const string &str){ //檢查是否為負(fù)數(shù)
return str[0]=='-';
}
//判斷是否為0,全部為0的數(shù)字就是0,比如00000
bool CheckZero(const string &str){ //檢查是否等于0
int size=str.size(); //如果全是0則為0,這里假設(shè)不會(huì)有帶符號(hào)的+00000或者-00000作為輸入
for(int i=0;i<size;i++)
if(str[i]!='0') return false;
return true;
}
string Add(string str1, string str2){
//關(guān)于0的處理
if(CheckZero(str1)&&CheckZero(str2)) return "0"; //如果都是0
else if(CheckZero(str1)) return str2; //如果有個(gè)一為0
else if(CheckZero(str2)) return str1;
string ans;
int i=str1.size()-1,j=str2.size()-1;
int flag=0; //進(jìn)位標(biāo)記
while(i>=0||j>=0||flag){
int numa=i>=0?str1[i--]-'0':0; //如果所有位都訪問完畢,對(duì)應(yīng)的位置用0代替
int numb=j>=0?str2[j--]-'0':0;
flag=numa+numb+flag; //計(jì)算兩位的和
ans+='0'+flag%10; //取個(gè)位保存在答案中
flag/=10; //計(jì)算進(jìn)位
}
reverse(ans.begin(),ans.end());
return ans;
}
string Mul(string str1, string str2){
if(CheckZero(str1)||CheckZero(str2)) return "0"; //如果有一個(gè)為0,則結(jié)果為0
int negative=0,negastr1=0,negastr2=0; //定正負(fù)
if(CheckNegative(str1)){ //確定正負(fù)號(hào)標(biāo)記,并且去掉-字符
negastr1=1; str1=str1.substr(1,str1.size()-1);
}
if(CheckNegative(str2)){
negastr2=1; str2=str2.substr(1,str2.size()-1);
}
negative=negastr1^negastr2; //異或運(yùn)算確定結(jié)果的正負(fù)號(hào)
string ans;
if(Lower(str1,str2)) swap(str1,str2); //保證str1大于等于str2
int size1=str1.size(),size2=str2.size();
for(int i=size2-1;i>=0;i--){ //遍歷較小數(shù)字的每一位
string temp(size2-1-i,'0'); //temp為str1乘以str2[i]的積,根據(jù)str2[i]的權(quán)重(個(gè)十百千萬,補(bǔ)充對(duì)應(yīng)個(gè)數(shù)的0)
int flag=0; //進(jìn)位標(biāo)記
for(int j=size1-1;j>=0;j--){ //temp
flag+=(str1[j]-'0')*(str2[i]-'0');
temp.push_back('0'+(flag%10));
flag/=10;
}
if(flag) temp.push_back('0'+flag); //如果最高位還有進(jìn)位
reverse(temp.begin(),temp.end());
ans=Add(ans,temp); //將計(jì)算結(jié)果累加到最終的結(jié)果上
}
if(negative) ans="-"+ans; //處理結(jié)果的正負(fù)號(hào)
return ans;
}
int main(){
cout << "請(qǐng)輸入兩個(gè)整數(shù):" << endl;
string a, b;
cin >> a >> b;
string result = Mul(a, b);
cout << "積:" << result << endl;
return 0;
}
6. 大整數(shù)的除法,計(jì)算結(jié)果精確到6位小數(shù)(6位小數(shù)后直接舍去)
如商3.700014[5800933124]
大數(shù)除法的實(shí)現(xiàn),同樣采用我們除法式子的方法進(jìn)行計(jì)算。首先,使用異或的方法確定結(jié)果的正負(fù)號(hào)。兩個(gè)數(shù)字相除的時(shí)候,如果第一個(gè)數(shù)字大于等于第二個(gè)數(shù)字,則結(jié)果一定是大于等于1的,否則小于1。于是,為了實(shí)現(xiàn)小于1的結(jié)果表示,我們?yōu)榻Y(jié)果精確到小數(shù)點(diǎn)后6位。這里采用的方法為:事先確定是否為純小數(shù),然后在第一個(gè)數(shù)字的末尾加上6個(gè)0,然后使用我們除法式子的方法進(jìn)行計(jì)算。從第一個(gè)數(shù)字的頭部開始,找到第一個(gè)長度能夠進(jìn)行商運(yùn)算的數(shù)字開始,計(jì)算商并將臨時(shí)的余數(shù)補(bǔ)足一位進(jìn)行下一位商的計(jì)算,直到計(jì)算完畢。
除法,可能遇到除數(shù)為0的情況,因此需要在計(jì)算的時(shí)候提前進(jìn)行判定。另外計(jì)算過程中,計(jì)算商的方法使用大數(shù)的減法操作,因此可能會(huì)遇到0堆積的情況(長度增大),會(huì)影響到大小的比較判定,要注意處理。
最后,我們考慮一些特殊情況,比如被除數(shù)為0的時(shí)候,可以直接輸出結(jié)果0.000000。
實(shí)現(xiàn)代碼
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
//判斷兩個(gè)超大正整數(shù)的大小
bool Lower(string str1, string str2){
//長度長的一定大(這里假設(shè)除了0以外,都沒有前導(dǎo)0),長度相等則字典序小的數(shù)字更小
return str1.size()<str2.size()||(str1.size()==str2.size()&&str1<str2);
}
//判斷是否為負(fù),只需要判斷第一位是不是負(fù)號(hào)即可,這里不考慮正號(hào)的存在,即認(rèn)為不使用正號(hào)
bool CheckNegative(const string &str){ //檢查是否為負(fù)數(shù)
return str[0]=='-';
}
//判斷是否為0,全部為0的數(shù)字就是0,比如00000
bool CheckZero(const string &str){ //檢查是否等于0
int size=str.size(); //如果全是0則為0,這里假設(shè)不會(huì)有帶符號(hào)的+00000或者-00000作為輸入
for(int i=0;i<size;i++)
if(str[i]!='0') return false;
return true;
}
//大數(shù)減法
string Sub(string str1, string str2){
//處理0的情況
if(str1==str2||(CheckZero(str1)&&CheckZero(str2))) return "0"; //如果兩數(shù)相等或者都是0
else if(CheckZero(str1)) return "-"+str2; //如果第一個(gè)數(shù)字為0
else if(CheckZero(str2)) return str1; //如果第二個(gè)數(shù)字為0
//定正負(fù)
int negative=0; //結(jié)果的正負(fù)號(hào)
if(Lower(str1,str2)){
swap(str1,str2); //保證str1大于str2
negative=1; //如果str1小于str2,則結(jié)果過為負(fù)值
}
string ans;
int i=str1.size()-1,j=str2.size()-1;//逆序開始處理
int flag=0; //借位標(biāo)記
while(i>=0||j>=0){
int numa=i>=0?str1[i--]-'0':0; //取每一位,因?yàn)殚L度可能不同所以當(dāng)某一個(gè)已經(jīng)讀取完畢時(shí)對(duì)應(yīng)位置取0
int numb=j>=0?str2[j--]-'0':0;
numa-=flag; //先減去借位
if(numa<numb){ //如果不夠減則向上一位借位(只可能借一位)
numa+=10; //借位并記錄借位
flag=1;
}
else flag=0; //如果不借位,則借位標(biāo)記為0
ans+='0'+numa-numb; //計(jì)算當(dāng)前位置并保存
}
i=ans.size()-1;
while(ans[i]=='0') i--;
ans=ans.substr(0,i+1); //去除前導(dǎo)0,如111-110=1
if(negative) ans+='-'; //如果計(jì)算結(jié)果是負(fù)數(shù),添加負(fù)數(shù)符號(hào)
reverse(ans.begin(),ans.end()); //因?yàn)槭悄嫘蛴?jì)算得到的結(jié)果,所以需要翻轉(zhuǎn)一下
return ans;
}
string Div(string str1, string str2){
//處理除數(shù)為0的情況和被除數(shù)為0的情況
if(CheckZero(str2)) return "The divisor cannot be zero!";
else if(CheckZero(str1)) return "0.000000";
int negative=0,negastr1=0,negastr2=0; //定正負(fù)
if(CheckNegative(str1)){ //確定正負(fù)號(hào)標(biāo)記,并且去掉-
negastr1=1; str1=str1.substr(1,str1.size()-1);
}
if(CheckNegative(str2)){
negastr2=1; str2=str2.substr(1,str2.size()-1);
}
negative=negastr1^negastr2; //異或運(yùn)算確定結(jié)果的正負(fù)號(hào)
int point=0; //結(jié)果是否為純小數(shù)
if(Lower(str1,str2)) point=1; //如果str1小于str2,則計(jì)算為純小數(shù)
string ans; //計(jì)算結(jié)果
str1+=string(6,'0'); //補(bǔ)足6個(gè)0,用于計(jì)算小數(shù)位
int size1=str1.size(),size2=str2.size();
int i=size2-1; //商第一位的位置
string temp=str1.substr(0,i); //從str1上取size2-1個(gè)字符
for(i;i<size1;i++){
temp+=str1[i]; //從后邊拿出一位,預(yù)先處理可以防止結(jié)尾處越界
int cnt=0; //當(dāng)前位的商,也就是temp中包含了多少個(gè)str2,使用減法 //如果temp不為0,則計(jì)算商
while(Lower(str2,temp)||temp==str2){ //如果當(dāng)前位商不為0,則計(jì)算商
temp=Sub(temp,str2);
cnt++;
}
if(temp=="0") temp.clear(); //如果某次計(jì)算結(jié)果為0,則清空,避免0的堆積,比如111000 111
ans.push_back('0'+cnt); //保存商
}
i=0;
while(ans[i]=='0') i++;
ans=ans.substr(i,ans.size()-i); //去除前導(dǎo)0
if(point){ //如果是純小數(shù),補(bǔ)足6位并添加小數(shù)點(diǎn)
int len=6-ans.size();
ans="0."+string(len,'0')+ans;
}
else ans.insert((ans.end()-6),'.'); //如果不是小數(shù),則只需要插入小數(shù)點(diǎn)
if(negative) ans="-"+ans; //最后一步驟,如果是負(fù)數(shù)帶上負(fù)號(hào)
return ans;
}
int main(){
cout << "請(qǐng)輸入兩個(gè)整數(shù):" << endl;
string a, b;
cin >> a >> b;
string result = Div(a, b);
cout << "商(6位小數(shù)后直接舍去):" << result << endl;
return 0;
}
7.最后,整合為大數(shù)四則運(yùn)算
加減運(yùn)算僅限非負(fù)整數(shù)的運(yùn)算,因?yàn)樨?fù)數(shù)的加減運(yùn)算可以等價(jià)為某種形式的加法或者減法運(yùn)算,故不做考慮;乘除運(yùn)算只考慮大整數(shù)運(yùn)算,不考慮小數(shù)的計(jì)算;除法只考慮大整數(shù)運(yùn)算,計(jì)算結(jié)果精確到6位小數(shù)(6位小數(shù)后直接舍去)。源碼如下:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
//大數(shù)四則運(yùn)算,兩個(gè)參數(shù)都不能為空
string Add(string str1, string str2); //大數(shù)加法
string Sub(string str1, string str2); //大數(shù)減法
string Mul(string str1, string str2); //大數(shù)乘法
string Div(string str1, string str2); //大數(shù)除法
bool Lower(string str1, string str2); //大數(shù)比較(小于)
bool CheckZero(const string &str); //檢查是不是0,比如0000認(rèn)為是0
bool CheckNegative(const string &str); //檢查是不是負(fù)數(shù)
void ShowMenu(); //提示菜單
void ShowMenu(char choice); //二級(jí)菜單
int main(){
string a,b;
char ch;
ShowMenu();
while(cin>>ch&&ch!='q'){ //循環(huán)打印菜單并提示用戶輸入
ShowMenu(ch);
cin>>a>>b;
switch(ch){
case 'a':cout<<a<<" + "<<b<<" = "<<Add(a,b)<<endl;break;
case 'b':cout<<a<<" - "<<b<<" = "<<Sub(a,b)<<endl;break;
case 'c':cout<<a<<" * "<<b<<" = "<<Mul(a,b)<<endl;break;
case 'd':cout<<a<<" / "<<b<<" = "<<Div(a,b)<<endl;break;
}
ShowMenu();
}
return 0;
}
string Add(string str1, string str2){
//關(guān)于0的處理
if(CheckZero(str1)&&CheckZero(str2)) return "0"; //如果都是0
else if(CheckZero(str1)) return str2; //如果有個(gè)一為0
else if(CheckZero(str2)) return str1;
string ans;
int i=str1.size()-1,j=str2.size()-1;
int flag=0; //進(jìn)位標(biāo)記
while(i>=0||j>=0||flag){
int numa=i>=0?str1[i--]-'0':0; //如果所有位都訪問完畢,對(duì)應(yīng)的位置用0代替
int numb=j>=0?str2[j--]-'0':0;
flag=numa+numb+flag; //計(jì)算兩位的和
ans+='0'+flag%10; //取個(gè)位保存在答案中
flag/=10; //計(jì)算進(jìn)位
}
reverse(ans.begin(),ans.end());
return ans;
}
string Sub(string str1, string str2){
//處理0的情況
if(str1==str2||(CheckZero(str1)&&CheckZero(str2))) return "0"; //如果兩數(shù)相等或者都是0
else if(CheckZero(str1)) return "-"+str2; //如果第一個(gè)數(shù)字為0
else if(CheckZero(str2)) return str1; //如果第二個(gè)數(shù)字為0
//定正負(fù)
int negative=0; //結(jié)果的正負(fù)號(hào)
if(Lower(str1,str2)){
swap(str1,str2); //保證str1大于str2
negative=1; //如果str1小于str2,則結(jié)果過為負(fù)值
}
string ans;
int i=str1.size()-1,j=str2.size()-1;//逆序開始處理
int flag=0; //借位標(biāo)記
while(i>=0||j>=0){
int numa=i>=0?str1[i--]-'0':0; //取每一位,因?yàn)殚L度可能不同所以當(dāng)某一個(gè)已經(jīng)讀取完畢時(shí)對(duì)應(yīng)位置取0
int numb=j>=0?str2[j--]-'0':0;
numa-=flag; //先減去借位
if(numa<numb){ //如果不夠減則向上一位借位(只可能借一位)
numa+=10; //借位并記錄借位
flag=1;
}
else flag=0; //如果不借位,則借位標(biāo)記為0
ans+='0'+numa-numb; //計(jì)算當(dāng)前位置并保存
}
i=ans.size()-1;
while(ans[i]=='0') i--;
ans=ans.substr(0,i+1); //去除前導(dǎo)0,如111-110=1
if(negative) ans+='-'; //如果計(jì)算結(jié)果是負(fù)數(shù),添加負(fù)數(shù)符號(hào)
reverse(ans.begin(),ans.end()); //因?yàn)槭悄嫘蛴?jì)算得到的結(jié)果,所以需要翻轉(zhuǎn)一下
return ans;
}
string Mul(string str1, string str2){
if(CheckZero(str1)||CheckZero(str2)) return "0"; //如果有一個(gè)為0,則結(jié)果為0
int negative=0,negastr1=0,negastr2=0; //定正負(fù)
if(CheckNegative(str1)){ //確定正負(fù)號(hào)標(biāo)記,并且去掉-字符
negastr1=1; str1=str1.substr(1,str1.size()-1);
}
if(CheckNegative(str2)){
negastr2=1; str2=str2.substr(1,str2.size()-1);
}
negative=negastr1^negastr2; //異或運(yùn)算確定結(jié)果的正負(fù)號(hào)
string ans;
if(Lower(str1,str2)) swap(str1,str2); //保證str1大于等于str2
int size1=str1.size(),size2=str2.size();
for(int i=size2-1;i>=0;i--){ //遍歷較小數(shù)字的每一位
string temp(size2-1-i,'0'); //temp為str1乘以str2[i]的積,根據(jù)str2[i]的權(quán)重(個(gè)十百千萬,補(bǔ)充對(duì)應(yīng)個(gè)數(shù)的0)
int flag=0; //進(jìn)位標(biāo)記
for(int j=size1-1;j>=0;j--){ //temp
flag+=(str1[j]-'0')*(str2[i]-'0');
temp.push_back('0'+(flag%10));
flag/=10;
}
if(flag) temp.push_back('0'+flag); //如果最高位還有進(jìn)位
reverse(temp.begin(),temp.end());
ans=Add(ans,temp); //將計(jì)算結(jié)果累加到最終的結(jié)果上
}
if(negative) ans="-"+ans; //處理結(jié)果的正負(fù)號(hào)
return ans;
}
string Div(string str1, string str2){
//處理除數(shù)為0的情況和被除數(shù)為0的情況
if(CheckZero(str2)) return "The divisor cannot be zero!";
else if(CheckZero(str1)) return "0.000000";
int negative=0,negastr1=0,negastr2=0; //定正負(fù)
if(CheckNegative(str1)){ //確定正負(fù)號(hào)標(biāo)記,并且去掉-
negastr1=1; str1=str1.substr(1,str1.size()-1);
}
if(CheckNegative(str2)){
negastr2=1; str2=str2.substr(1,str2.size()-1);
}
negative=negastr1^negastr2; //異或運(yùn)算確定結(jié)果的正負(fù)號(hào)
int point=0; //結(jié)果是否為純小數(shù)
if(Lower(str1,str2)) point=1; //如果str1小于str2,則計(jì)算為純小數(shù)
string ans; //計(jì)算結(jié)果
str1+=string(6,'0'); //補(bǔ)足6個(gè)0,用于計(jì)算小數(shù)位
int size1=str1.size(),size2=str2.size();
int i=size2-1; //商第一位的位置
string temp=str1.substr(0,i); //從str1上取size2-1個(gè)字符
for(i;i<size1;i++){
temp+=str1[i]; //從后邊拿出一位,預(yù)先處理可以防止結(jié)尾處越界
int cnt=0; //當(dāng)前位的商,也就是temp中包含了多少個(gè)str2,使用減法 //如果temp不為0,則計(jì)算商
while(Lower(str2,temp)||temp==str2){ //如果當(dāng)前位商不為0,則計(jì)算商
temp=Sub(temp,str2);
cnt++;
}
if(temp=="0") temp.clear(); //如果某次計(jì)算結(jié)果為0,則清空,避免0的堆積,比如111000 111
ans.push_back('0'+cnt); //保存商
}
i=0;
while(ans[i]=='0') i++;
ans=ans.substr(i,ans.size()-i); //去除前導(dǎo)0
if(point){ //如果是純小數(shù),補(bǔ)足6位并添加小數(shù)點(diǎn)
int len=6-ans.size();
ans="0."+string(len,'0')+ans;
}
else ans.insert((ans.end()-6),'.'); //如果不是小數(shù),則只需要插入小數(shù)點(diǎn)
if(negative) ans="-"+ans; //最后一步驟,如果是負(fù)數(shù)帶上負(fù)號(hào)
return ans;
}
bool Lower(string str1, string str2){ //長度長的一定大(這里假設(shè)除了0以外,都沒有前導(dǎo)0),長度相等則字典序小的數(shù)字更小
return str1.size()<str2.size()||(str1.size()==str2.size()&&str1<str2);
}
bool CheckZero(const string &str){ //檢查是否等于0
int size=str.size(); //如果全是0則為0,這里假設(shè)不會(huì)有帶符號(hào)的+00000或者-00000作為輸入
for(int i=0;i<size;i++)
if(str[i]!='0') return false;
return true;
}
bool CheckNegative(const string &str){ //檢查是否為負(fù)數(shù)
return str[0]=='-';
}
void ShowMenu(){
cout<<"請(qǐng)選擇要進(jìn)行的大數(shù)運(yùn)算:\n"
<<"a) 加法 b) 減法\n"
<<"c) 乘法 d) 除法\n"
<<"q) 退出\n"
<<"請(qǐng)輸入你的選擇: ";
}
void ShowMenu(char choice){
cout<<"請(qǐng)輸入要計(jì)算的兩個(gè)數(shù)字";
switch(choice){
case 'a':cout<<"(僅支持非負(fù)整數(shù)加法計(jì)算): "<<endl;break;
case 'b':cout<<"(僅支持非負(fù)整數(shù)減法計(jì)算): "<<endl;break;
case 'c':cout<<"(僅支持整數(shù)乘法計(jì)算): "<<endl;break;
case 'd':cout<<"(僅支持整數(shù)除法計(jì)算,計(jì)算結(jié)果保留6位小數(shù),之后的直接舍棄): "<<endl;break;
}
}
附錄、進(jìn)一步學(xué)習(xí)了解
https://blog.csdn.net/weixin_44668898/article/details/96763177文章來源:http://www.zghlxwxcb.cn/news/detail-708897.html
https://blog.csdn.net/wyqxii/article/details/131965735文章來源地址http://www.zghlxwxcb.cn/news/detail-708897.html
到了這里,關(guān)于C++中如何處理超長的數(shù)字(long long類型的整數(shù)都無法存儲(chǔ)的)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!