假如我們遇到了這樣一道題:
【題目描述】
給你一個浮點數f,輸出它保留n位小數(四舍五入)后的結果。
【輸入格式】
輸入兩個數,分別為f和n。
【輸出格式】
一個數,即最終結果。
【輸入樣例】
3.15 1
【輸出樣例】
3.2
【說明】
f可以用double儲存,1<=n<=5。
看過題后,LYF dalao吐槽到:
fAKe?這么簡單?信不信老子用C++30s過。。。
(然而事情並沒有這么簡單。。。)
LYF dalao似乎在20后就寫出來了:
#include <iostream>
#include <iomanip>
using namespace std;
int n;
double f;
int main()
{
cin >> f >> n;
cout << fixed << setprecision(n) << f << endl;
return 0;
}
LYF他測試了樣例,結果是:
(以下是控制台內容)
3.15 1
3.1--------------------------------
Process exited after -INF seconds with return value 0
請按任意鍵繼續. . .
(不信的同學可以到這里試一試)
LYF又是一頓吐槽:
蛤?這是啥玩意啊?用printf試試。。。
他又寫了一份代碼:
#include <iostream>
#include <cstdio>
using namespace std;
int n;
double f;
char com[7]="%. lf\n";
int main()
{
cin >> f >> n;
com[2] = n + '0';
printf(com, f);
return 0;
}
結果。。。毫無變化
(以下是控制台內容)
3.15 1
3.1--------------------------------
Process exited after -INF seconds with return value 0
請按任意鍵繼續. . .
(同樣,不信的話可以到這里試試)
LYF dalao陷入了崩潰狀態。。。
那么,我們回首這場悲劇,來探究問題的根源。
蒟蒻我寫了一份代碼,來研究C++/C(cout/printf)在對浮點數進行四舍五入Output時情況:
#include <iostream>
#include <iomanip>
#include <cstdio>
using namespace std;
char com[7]="%. lf\n";
int main()
{
cout << "It's cout:" << endl;
out << fixed << setprecision(1) << "base: " << 3.15 << endl;
cout << fixed << setprecision(1) << "test1:" << 3.150 << endl;
cout << fixed << setprecision(1) << "test2:" << 3.153 << endl;
cout << fixed << setprecision(1) << "test3:" << 3.155 << endl;
cout << fixed << setprecision(1) << "test4:" << 3.159 << endl;
cout << fixed << setprecision(2) << "test5:" << 3.155 << endl;
cout << fixed << setprecision(2) << "test6:" << 3.15533 << endl;
cout<<endl;
printf("It's printf\n");
printf("base: ");
com[2] = 1 + '0';
printf(com, 3.15);
printf("test1:");
com[2] = 1 + '0';
printf(com, 3.150);
printf("test1:");
com[2] = 1 + '0';
printf(com, 3.153);
printf("test1:");
com[2] = 1 + '0';
printf(com, 3.155);
printf("test1:");
com[2] = 1 + '0';
printf(com, 3.159);
printf("test1:");
com[2] = 2 + '0';
printf(com, 3.155);
printf("test1:");
com[2] = 2 + '0';
printf(com, 3.15533);
return 0;
}
(老規矩,發放測試入口)
運行結果如下:
(以下是控制台內容)
It's cout:
base: 3.1
test1:3.1
test2:3.2
test3:3.2
test4:3.2
test5:3.15
test6:3.16It's printf
base: 3.1
test1:3.1
test1:3.2
test1:3.2
test1:3.2
test1:3.15
test1:3.16--------------------------------
Process exited after 0.INF seconds with return value 0
請按任意鍵繼續. . .
當我們分析上面的運行結果后,可總結如下規律:
- C++/C(cout/printf)在對浮點數進行四舍五入Output時所發生的現象一樣,即進行的操作相同。
- 若要四舍五入保留n位小數,當小數后第n+1位數x滿足0<=x<=4或6<=x<=9時,C++/C可正確四舍五入,輸出正確答案。
- 當x=5時,若第n+1位后還有數字(如測試中的3.153),可正確四舍五入;但是如果沒有(如測試中的3.15),可能會輸出錯誤答案(至於為什么是“可能”,因為我后來發現這種情況下不一定會錯)。
由此,我們可以發現,cout/printf在保留小數的時候,有非常大的問題(后來我了解到C++/C的機制是四舍六入五成雙,不是四舍五入),所以我們最好不要直接用它們四舍五入了。
蛤?你問我該用啥?
當然是這個啦:
inline double FourCutFiveKeep(double num,int t)
{
int pow10t=1;
while(t!=0)
{
pow10t *= 10;
t--;
}
return (int)(num * (double)pow10t + 0.5) / (double)pow10t;
}
(於是,我們有了一開始那道題的正解)
異 同步發表於:洛谷