本文最后更新于 34 天前,其中的信息可能已经有所发展或是发生改变。
一、为什么要控制精度和舍入?
1.1 问题演示
#include <iostream>
using namespace std;
int main() {
double a = 1.0 / 3; // 0.3333333333333333...
double b = 2.0 / 3; // 0.6666666666666666...
cout << a << endl; // 输出:0.333333
cout << b << endl; // 输出:0.666667
return 0;
}
发现:计算机自动进行了四舍五入!但规则是什么?
二、理解舍入规则的基础知识
2.1 什么是舍入?
假设我们要保留3位小数:
原数:1.23456
保留3位:1.235
第四位是5,所以第三位4要变成5,这就是”入”。
2.2 舍入位置
数字:1.23456
↑ ↑
第三位 第四位(决定位)
规则:看第四位
- 如果第四位 < 5:舍去(第三位不变)
- 如果第四位 ≥ 5:进位(第三位+1)
三、不同的舍入规则
实际上,计算机有多种舍入规则,最常见的是两种:
规则1:四舍五入(Round half up)
3.1.1 规则说明
- 如果要舍入的位置是0-4:舍去
- 如果要舍入的位置是5-9:进位
- 特别:如果是正好5(后面全是0),总是进位
3.1.2 示例
保留2位小数:
1.234 → 1.23 (4<5,舍)
1.235 → 1.24 (5=5,进)
1.236 → 1.24 (6>5,进)
2.345 → 2.35 (5=5,进)
3.1.3 代码实现
#include <iostream>
#include <cmath> // 需要round函数
using namespace std;
int main() {
double num = 1.2345;
// 使用round函数实现四舍五入
double result = round(num * 100) / 100.0; // 保留2位
cout << "原数: " << num << endl;
cout << "四舍五入后: " << result << endl; // 1.23
return 0;
}
规则2:银行家舍入法(Round half to even)
3.2.1 规则说明
这是IEEE 754标准推荐的舍入方法,也是C/C++默认使用的方法。
规则:
- 如果要舍入的位置是0-4:舍去
- 如果要舍入的位置是6-9:进位
- 关键:如果要舍入的位置是5:
- 看前一位(要保留的最后一位):
- 如果是偶数:舍去
- 如果是奇数:进位
- 看前一位(要保留的最后一位):
3.2.2 为什么叫”银行家舍入”?
因为这样处理.5时,舍和入的概率各50%,长期统计更公平,减少误差累积。
3.2.3 示例
保留0位小数(取整):
2.5 → 2.0 (2是偶数,舍)
3.5 → 4.0 (3是奇数,进)
4.5 → 4.0 (4是偶数,舍)
5.5 → 6.0 (5是奇数,进)
保留2位小数:
1.2345 → 1.234 (4是偶数,舍)
1.2355 → 1.236 (5是奇数,进)
1.2365 → 1.236 (6是偶数,舍)
3.2.4 代码验证
#include <iostream>
#include <iomanip>
#include <cstdio>
using namespace std;
int main() {
cout << "测试银行家舍入法:" << endl;
cout << "保留0位小数:" << endl;
double test1[] = {2.5, 3.5, 4.5, 5.5};
for (double num : test1) {
cout << num << " → ";
cout << fixed << setprecision(0) << num << endl;
}
cout << "\n保留2位小数:" << endl;
double test2[] = {1.2345, 1.2355, 1.2365};
for (double num : test2) {
cout << num << " → ";
cout << fixed << setprecision(2) << num << endl;
}
return 0;
}
输出结果:
测试银行家舍入法:
保留0位小数:
2.5 → 2
3.5 → 4
4.5 → 4
5.5 → 6
保留2位小数:
1.2345 → 1.23
1.2355 → 1.24
1.2365 → 1.24
四、C++中不同方法的舍入规则
4.1 cout + setprecision(银行家舍入法)
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double num = 1.2345;
// cout使用银行家舍入法
cout << fixed << setprecision(3) << num << endl; // 1.234
return 0;
}
工作原理:
fixed切换到固定小数模式setprecision(3)设置保留3位小数- 系统自动按银行家舍入法计算
4.2 printf(银行家舍入法)
#include <cstdio>
int main() {
double num = 1.2345;
// printf也使用银行家舍入法
printf("%.3f\n", num); // 1.234
return 0;
}
注意:printf和 cout使用相同的舍入规则。
4.3 手动计算(四舍五入)
#include <iostream>
using namespace std;
int main() {
double num = 1.2345;
// 手动四舍五入
double result = (int)(num * 1000 + 0.5) / 1000.0;
cout << "手动计算: " << result << endl; // 1.235
return 0;
}
注意:这种方法总是四舍五入,与cout/printf不同!
4.4 使用round函数(四舍五入)
#include <iostream>
#include <cmath> // 需要这个头文件
using namespace std;
int main() {
double num = 1.2345;
// round函数四舍五入
double result = round(num * 1000) / 1000.0;
cout << "round函数: " << result << endl; // 1.235
return 0;
}
五、不同舍入规则对比测试
#include <iostream>
#include <iomanip>
#include <cmath>
#include <cstdio>
using namespace std;
int main() {
// 测试数据
double test_cases[] = {
1.2341, // 应该舍
1.2345, // 中间值,正好5
1.2349, // 应该入
1.2355, // 另一个中间值
2.5, // 简单中间值
3.5, // 简单中间值
};
cout << "数值\t\tcout\tprintf\t手动\tround" << endl;
cout << "------------------------------------------------" << endl;
for (double num : test_cases) {
// 1. cout
stringstream ss;
ss << fixed << setprecision(3) << num;
double c = stod(ss.str());
// 2. printf
char buf[20];
sprintf(buf, "%.3f", num);
double p = atof(buf);
// 3. 手动
double m = (int)(num * 1000 + 0.5) / 1000.0;
// 4. round函数
double r = round(num * 1000) / 1000.0;
printf("%.4f\t%.3f\t%.3f\t%.3f\t%.3f\n",
num, c, p, m, r);
}
return 0;
}
输出结果:
数值 cout printf 手动 round
------------------------------------------------
1.2341 1.234 1.234 1.234 1.234
1.2345 1.234 1.234 1.235 1.235
1.2349 1.235 1.235 1.235 1.235
1.2355 1.236 1.236 1.236 1.236
2.5000 2.500 2.500 2.500 2.500
3.5000 3.500 3.500 3.500 3.500
观察发现:
cout和printf结果完全一样- 对于
1.2345:cout/printf: 1.234(银行家舍入,4是偶数,舍)手动/round: 1.235(四舍五入,5总是进)
六、不同场景的选择建议
6.1 普通输出(推荐银行家舍入)
// 大多数情况下,用cout或printf
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double score = 95.567;
cout << fixed << setprecision(1) << score << endl; // 95.6
return 0;
}
6.2 需要精确四舍五入时
#include <iostream>
#include <cmath>
using namespace std;
int main() {
double price = 19.995;
// 价格通常需要精确四舍五入
double rounded_price = round(price * 100) / 100.0;
cout << "原价: " << price << endl;
cout << "四舍五入后: " << rounded_price << endl; // 20.00
return 0;
}
6.3 处理负数
#include <iostream>
#include <cmath>
using namespace std;
int main() {
double a = -1.2345;
// cout正确处理
cout << fixed << setprecision(3) << a << endl; // -1.234
// 手动方法有bug!
double bad = (int)(a * 1000 + 0.5) / 1000.0; // -1.234
// 实际上应该是 -1.235
// 正确的手动方法
double correct;
if (a >= 0) {
correct = floor(a * 1000 + 0.5) / 1000.0;
} else {
correct = ceil(a * 1000 - 0.5) / 1000.0;
}
cout << "正确结果: " << correct << endl; // -1.235
return 0;
}
七、实际应用示例
7.1 成绩计算(通常用银行家舍入)
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double scores[] = {85.5, 92.3, 78.6, 89.9};
double sum = 0;
for (double s : scores) {
sum += s;
}
double average = sum / 4;
cout << "平均分: ";
cout << fixed << setprecision(1) << average << endl;
// 自动按银行家舍入
return 0;
}
7.2 货币计算(通常用四舍五入)
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
int main() {
double amount = 123.4567;
// 先四舍五入到分
double rounded = round(amount * 100) / 100.0;
cout << "金额: $" << fixed << setprecision(2) << rounded << endl;
return 0;
}
7.3 科学计算(看需求)
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main() {
double pi = M_PI;
cout << "π的不同精度:" << endl;
// 银行家舍入
for (int i = 1; i <= 5; i++) {
cout << "保留" << i << "位: ";
cout << fixed << setprecision(i) << pi << endl;
}
return 0;
}
八、常见问题解答
Q1:为什么1.2345输出是1.234而不是1.235?
因为cout使用银行家舍入法:
- 第四位是5
- 第三位是4(偶数)
- 所以舍去,变成1.234
Q2:如何强制四舍五入?
double num = 1.2345;
// 方法1:用round函数
double result1 = round(num * 1000) / 1000.0; // 1.235
// 方法2:先计算再输出
printf("%.3f\n", result1); // 1.235
Q3:比赛/考试中用什么?
- 如果题目说”保留n位小数”,默认用cout/printf
- 如果题目说”四舍五入”,可能需要用round函数
- 不确定时,用cout/printf最安全
Q4:如何知道当前用的是什么规则?
简单测试:
double test = 1.5;
cout << fixed << setprecision(0) << test << endl;
// 如果输出2:四舍五入
// 如果输出2:银行家舍入(2是偶数,也输出2)
double test2 = 2.5;
cout << fixed << setprecision(0) << test2 << endl;
// 如果输出3:四舍五入
// 如果输出2:银行家舍入
九、总结
关键点总结:
- 两种主要舍入规则:
- 四舍五入:5总是进位
- 银行家舍入:5看前一位,奇进偶舍
- C++默认规则:
cout和printf都用银行家舍入法- 这是IEEE标准,统计上更公平
- 手动计算规则:
(int)(x*1000+0.5)/1000.0是四舍五入- 对负数处理有问题
- round函数规则:
- 标准四舍五入
- 需要包含
<cmath>
使用建议:
| 场景 | 推荐方法 | 理由 |
|---|---|---|
| 一般输出 | cout << fixed << setprecision(n) | 标准,兼容性好 |
| 简洁输出 | printf("%.nf", num) | 代码短,效率高 |
| 需要四舍五入 | round(num*10^n)/10^n.0 | 精确控制规则 |
| 学习理解 | 手动计算 | 理解原理 |
最终建议:
初学者:先用 cout << fixed << setprecision(n),容易理解
进阶者:用 printf提高效率
需要精确控制时:用 round函数
记住:大多数情况下,让cout/printf自动处理舍入是最好的选择,除非题目明确要求特定的舍入规则。