C 班 第 7 课:小数与平均数——整数装不下的时候

适用对象: 完成了第 6 课(数组)的同学 使用方式: 自学讲义。放在电脑旁边,边读边照着做 学完本课,你应该能:

  1. 说清楚为什么 7 / 2 的结果是 3 而不是 3.5
  2. double 存小数,并让除法算出正确的小数结果
  3. 把一个小数保留两位输出
  4. 求出一串成绩的平均分

遇到问题怎么办: 前面六节课我们只用过 int,它只能装整数。今天要还两笔账:第 2 课说过"除不尽以后学",第 6 课说过"求平均数要用小数,以后学"。今天一起还清。


1. 热身:把上节课的求总和再跑一遍(10 分钟)

打开上节课的 t2.cpp(求一串数的总和),重新运行一遍。输入 53 1 4 1 5,应该输出 14

跑通了就好。现在换个问题:这 5 个数的平均数是多少?

平均数就是总和除以个数,也就是 14 / 5。拿计算器一按,是 2.8。

那就写个程序算算看:

#include <bits/stdc++.h>
using namespace std;

int main() {
    int sum = 14;
    int n = 5;
    cout << sum / n << endl;
    return 0;
}

运行结果是——

2

不是 2.8,是 2。小数点后面那个 8 不见了。

这就是今天要解决的问题。


2. 整数除法:小数部分被扔掉了(15 分钟)

先看清楚这个现象。运行下面这个程序:

#include <bits/stdc++.h>
using namespace std;

int main() {
    cout << 7 / 2 << endl;
    cout << 9 / 4 << endl;
    cout << 1 / 3 << endl;
    return 0;
}

输出:

3
2
0

7 除以 2 应该是 3.5,它给了 3;9 除以 4 应该是 2.25,它给了 2;1 除以 3 应该是 0.333…,它直接给了 0

⚠️ 两个整数相除,结果也是整数。小数部分不是四舍五入,是直接扔掉。 7 / 2 = 3.5,扔掉 .5,得 3;9 / 4 = 2.25,扔掉 .25,得 2。哪怕是 2.99,扔掉之后也只剩 2。

为什么会这样?因为 72 都是整数,C++ 觉得"你给我两个整数,那我还你一个整数"。它压根就没打算算出小数来。

📝 这不是 bug,是 C++ 故意的。 第 3 课学的 % 求余数就靠这个规矩:7 / 2 得 3(商),7 % 2 得 1(余数),3 × 2 + 1 = 7,正好对上。

那想要 3.5 该怎么办?得先有一种能装小数的盒子


3. double:能装小数的盒子(20 分钟)

前面六课,我们造盒子只用过一个词:int

int a = 5;        // 装整数的盒子

今天认识第二种盒子:double,装小数的盒子

double x = 3.5;   // 装小数的盒子

用法和 int 一模一样——能存、能取、能算、能 cin 读进来、能 cout 打出去:

#include <bits/stdc++.h>
using namespace std;

int main() {
    double x = 3.5;
    double y = 2.0;
    cout << x << endl;
    cout << x + y << endl;
    cout << x * y << endl;
    return 0;
}

输出:

3.5
5.5
7

📝 double 这个名字不用去想它的意思,就当它是"小数盒子"的代号,跟 int 是"整数盒子"的代号一样。

关键:怎么让除法算出小数

光把结果存进 double没用的。看这个:

#include <bits/stdc++.h>
using namespace std;

int main() {
    double ans = 7 / 2;      // 想要 3.5
    cout << ans << endl;
    return 0;
}

输出还是:

3

⚠️ 因为 C++ 是先算等号右边、再往左边的盒子里放。 右边的 7 / 2 是两个整数相除,当场就算成了 3,小数部分那时候就已经扔掉了。等它进到 double 盒子里,早就没救了——盒子再能装小数,装进来的也只是个 3。

真正的办法:让参与除法的两个数里,至少有一个是小数。

#include <bits/stdc++.h>
using namespace std;

int main() {
    cout << 7 / 2 << endl;       // 两个都是整数 → 3
    cout << 7.0 / 2 << endl;     // 左边是小数 → 3.5
    cout << 7 / 2.0 << endl;     // 右边是小数 → 3.5
    return 0;
}

输出:

3
3.5
3.5

📝 77.0 在 C++ 眼里是两样东西。 多写一个 .0,它就从整数变成了小数,除法的规矩也跟着变了。

变量怎么办?

上面那招是数字写死的情况。可要是像热身那样,除号两边都是变量 sumn,总不能在变量后面加 .0 吧?

办法是:先乘以 1.0

#include <bits/stdc++.h>
using namespace std;

int main() {
    int sum = 14;
    int n = 5;
    cout << sum * 1.0 / n << endl;
    return 0;
}

输出:

2.8

成了。sum * 1.0 的意思是"把 sum 乘以 1,值一点没变,但它现在是个小数了",接下来的除法自然就按小数算。

⚠️ * 1.0 必须写在除号前面。 写成 sum / n * 1.0 就完蛋了——C++ 从左往右算,先做 sum / n 得到整数 2,再乘 1.0 变成 2.0,小数部分早在第一步就丢了。

📝 记死这个套路:sum * 1.0 / n 先乘 1.0,再除。

动手检查点 1(5 分钟)

新建 check1.cpp,把下面三行都输出出来,亲眼看看区别:

5 / 2
5.0 / 2
5 / 2.0

答案应该是 22.52.5


4. 保留两位小数(15 分钟)

小数能算出来了,但输出的样子还不太听话。试试这个:

#include <bits/stdc++.h>
using namespace std;

int main() {
    cout << 1 * 1.0 / 3 << endl;
    return 0;
}

输出:

0.333333

一口气六位。要是题目只要两位怎么办?

cout 前面加一句咒语:

#include <bits/stdc++.h>
using namespace std;

int main() {
    cout << fixed << setprecision(2);      // ← 咒语,写一次就够
    cout << 1 * 1.0 / 3 << endl;
    cout << 2 * 1.0 / 3 << endl;
    cout << 14 * 1.0 / 5 << endl;
    return 0;
}

输出:

0.33
0.67
2.80

三件事值得注意:

📝 要保留几位就把括号里的数字改成几。 保留一位写 setprecision(1),保留三位写 setprecision(3)

⚠️ fixed 不能漏。 只写 setprecision(2) 不写 fixed,它数的就不是小数点后面的位数了,2.8 会被打成 2.882.2 会被打成 82——完全不是你要的。两个词一起写,当成一句话背下来。

动手检查点 2(5 分钟)

新建 check2.cpp,用上面的咒语,把 10 * 1.0 / 3 保留两位输出。答案应该是 3.33


5. 求平均分:把账还上(15 分钟)

现在把上节课的数组和今天的小数合起来,做第 6 课欠下的那道题。

读入 n 个成绩,求平均分,保留两位小数:

#include <bits/stdc++.h>
using namespace std;

int a[105];

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];

    int sum = 0;                                  // 老套路:先求总和
    for (int i = 0; i < n; i++) sum = sum + a[i];

    cout << fixed << setprecision(2);
    cout << sum * 1.0 / n << endl;                // 再除以个数
    return 0;
}

输入 590 85 78 92 66,输出:

82.20

拿计算器验一下:90 + 85 + 78 + 92 + 66 = 411,411 ÷ 5 = 82.2。对上了。

📝 注意成绩本身还是 int 成绩都是整数,没必要改成 double只有除出来的那个结果才是小数。今天的新东西只用在最后一行。


6. 读错误:2 个典型坑(10 分钟)

错误 1:以为盒子是 double 就万事大吉(本课头号错误)

int sum = 411;
int n = 5;
double avg = sum / n;          ← 右边两个整数,当场算成 82
cout << fixed << setprecision(2) << avg << endl;

输出 82.00

这个错误最阴险:编译器一声不吭,程序照常运行,还煞有介事地给你打了两位小数——只不过小数点后面永远是 00

📝 看见 .00 结尾的平均分,第一反应就是去检查除号那一行有没有 * 1.0

错误 2:忘了写 fixed

cout << setprecision(2);       ← 漏了 fixed
cout << 82 * 1.0 / 5 << endl;

输出 16 而不是 16.40

没有 fixed 的时候,setprecision(2) 数的是整个数字一共几位,不是小数点后面几位。数字一大还会变成 1.23457e+06 这种看不懂的样子。

📝 fixed << setprecision(2) 是一句话,不是两句话。 少一个词,输出就面目全非。


7. 本课练习(30 分钟起步)

⭐ 基础题 1:两个数的平均数(文件名 t1.cpp

输入两个整数,输出它们的平均数,保留两位小数。

输入样例:
7 2

输出样例:
4.50
参考答案
#include <bits/stdc++.h>
using namespace std;

int main() {
    int a, b;
    cin >> a >> b;
    cout << fixed << setprecision(2);
    cout << (a + b) * 1.0 / 2 << endl;
    return 0;
}

⚠️ (a + b) 的括号不能省。写成 a + b * 1.0 / 2 的话,C++ 会先算 b * 1.0 / 2(先乘除后加减,跟数学课一样),结果变成 7 + 1 = 8,全错。


⭐⭐ 实战题 2:求平均分(文件名 t2.cpp

先输入一个整数 n,再输入 n 个整数成绩,输出平均分,保留两位小数。

输入样例:
5
90 85 78 92 66

输出样例:
82.20
参考答案
#include <bits/stdc++.h>
using namespace std;

int a[105];

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];

    int sum = 0;
    for (int i = 0; i < n; i++) sum = sum + a[i];

    cout << fixed << setprecision(2);
    cout << sum * 1.0 / n << endl;
    return 0;
}

和讲义第 5 节一模一样,这题就是让你亲手再敲一遍。敲完对照一下,看有没有把 * 1.0 漏掉。


⭐⭐⭐ 冲刺题 3:几个人高于平均分(文件名 t3.cpp

先输入一个整数 n,再输入 n 个整数成绩。输出平均分(保留两位小数),以及有几个人的成绩高于平均分

输入样例:
5
90 85 78 92 66

输出样例:
平均分是 82.20
高于平均分的有 3 人
参考答案
#include <bits/stdc++.h>
using namespace std;

int a[105];

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];

    int sum = 0;
    for (int i = 0; i < n; i++) sum = sum + a[i];

    double avg = sum * 1.0 / n;             // 先把平均分存起来

    int cnt = 0;                            // 计数器清零
    for (int i = 0; i < n; i++) {           // 再从头把数组走一遍
        if (a[i] > avg) cnt++;
    }

    cout << fixed << setprecision(2);
    cout << "平均分是 " << avg << endl;
    cout << "高于平均分的有 " << cnt << " 人" << endl;
    return 0;
}

这题的关键是数组要走两遍:第一遍不走完就不知道平均分是多少,知道了平均分才能开始第二遍数人头。这也正是上节课说的"先存进数组"的好处——数还在盒子里,想走几遍就走几遍。

⚠️ 别把 double avg 写成 int avg。写成 int 的话 82.2 会被砍成 82,而 78 这些正好卡在中间的成绩就可能数错。


本课要点速查

写法 意思
double x; 造一个能装小数的盒子
double x = 3.5; 造盒子并装进 3.5
cin >> x; 读一个小数进去(和 int 一样用)
7 / 2 两个整数相除 → 3,小数被扔掉
7.0 / 2 有一边是小数 → 3.5
sum * 1.0 / n 变量相除要小数结果,就这么写
cout << fixed << setprecision(2); 从这行起,所有小数都按两位输出

必背套路:

求平均数:   cout << fixed << setprecision(2);
             cout << sum * 1.0 / n << endl;

保留 n 位:  setprecision(n),前面的 fixed 不能漏

易错点:

症状 原因
平均分永远是 .00 结尾 除号两边都是整数,忘了 * 1.0
结果比正确答案小一点,还是个整数 double avg = sum / n; 右边先按整数算完了
输出 16 而不是 16.40 只写了 setprecision,漏了 fixed
输出变成 1.23457e+06 同上,漏了 fixed
a + b * 1.0 / 2 算出来不对 忘了加括号,先乘除后加减

以后学: 把一串数从小到大排序、二维数组(表格)、字符串、(double) 这种更正规的转换写法、小数的精度问题(两个小数不能直接用 == 比是否相等)。


结束前的自我检查

  1. 不看讲义,说出 9 / 49.0 / 4 分别是多少,以及为什么不一样
  2. 说清楚为什么 double avg = sum / n; 救不回小数
  3. 练习 1、2 都运行成功;⭐⭐⭐ 数人头能做出来最好
  4. 把练习 2 里的 sum * 1.0 / n 改成 sum / n 跑一遍,亲眼看看 82.20 变成 82.00
  5. 再把 fixed 删掉跑一遍,看看输出变成什么样