2.6 算術運算式

基本算術運算子

C++ 的五個基本算術運算子,跟數學課的加、減、乘、除很像,只是除法要特別小心

  • +(加)、-(減)、*(乘):和數學一樣。注意乘號是 *,不是 ×
  • /(除):當左右兩邊都是整數時,做的是整數除法——只取「商」、把小數部分直接丟掉。所以 7 / 3 不是 2.33,而是 2
  • %(取餘,也叫「模」):取整數除法之後的餘數。所以 7 % 31

/% 可以用「分東西」來想:把 7 顆糖果平分給 3 個人,每人拿 7 / 3 = 2 顆,最後剩下 7 % 3 = 1 顆。

運算子 名稱 例子 結果
+ 加法 7 + 3 10
- 減法 7 - 3 4
* 乘法 7 * 3 21
/ 除法(整數除法) 7 / 3 2(小數丟掉,只取商)
% 取餘(模) 7 % 3 1(餘數)
#include <iostream>
using namespace std;

int main() {
    int a = 17, b = 5;

    cout << a << " + " << b << " = " << (a + b) << endl;
    cout << a << " - " << b << " = " << (a - b) << endl;
    cout << a << " * " << b << " = " << (a * b) << endl;
    cout << a << " / " << b << " = " << (a / b) << endl;  // 整數除法(只取商)
    cout << a << " % " << b << " = " << (a % b) << endl;  // 取餘(餘數)

    return 0;
}

執行結果:

17 + 5 = 22
17 - 5 = 12
17 * 5 = 85
17 / 5 = 3
17 % 5 = 2

整數除法與取餘的細節

整數除法是「向零取整」

整數除法把小數丟掉之後,結果會落在哪個整數?規則是:朝數線上「0」的方向縮,停在離 0 比較近的那個整數——這就是「向零取整」名稱的由來。

  • 7 / 2 = 3.5,朝 0 縮 → 3
  • -7 / 2 = -3.5,朝 0 縮 → -3不是 -4;-3 比 -4 更靠近 0)
圖 2-2:整數除法向零取整
#include <iostream>
using namespace std;

int main() {
    cout << "7 / 2 = " << 7 / 2 << endl;      // 3.5 → 3
    cout << "-7 / 2 = " << -7 / 2 << endl;    // -3.5 → -3(朝 0,不是 -4)
    cout << "7 / -2 = " << 7 / -2 << endl;    // -3.5 → -3
    cout << "-7 / -2 = " << -7 / -2 << endl;  // 3.5 → 3
    return 0;
}

執行結果:

7 / 2 = 3
-7 / 2 = -3
7 / -2 = -3
-7 / -2 = 3

取餘的規則

由上面那條等式推回去,在 C++ 裡 a % b 的結果符號會和被除數 a 相同

#include <iostream>
using namespace std;

int main() {
    cout << "7 % 3 = " << 7 % 3 << endl;      // 1
    cout << "-7 % 3 = " << -7 % 3 << endl;    // -1
    cout << "7 % -3 = " << 7 % -3 << endl;    // 1
    cout << "-7 % -3 = " << -7 % -3 << endl;  // -1
    return 0;
}

執行結果:

7 % 3 = 1
-7 % 3 = -1
7 % -3 = 1
-7 % -3 = -1

運算子優先級

和數學課一樣,乘、除、取餘(*/%)都比加、減先算——這三個是同一個優先級(取餘並不特別),+- 也是同一級,同級的由左往右算。例如:

#include <iostream>
using namespace std;

int main() {
    // 2 + 3 * 4 先算 3 * 4 = 12,再加 2 = 14
    cout << "2 + 3 * 4 = " << 2 + 3 * 4 << endl;

    // 用括號改變順序
    cout << "(2 + 3) * 4 = " << (2 + 3) * 4 << endl;

    return 0;
}

執行結果:

2 + 3 * 4 = 14
(2 + 3) * 4 = 20

改變這個順序,就用小括號 ( ) 把想先算的部分括起來——括號裡的最先算

整數除法與取餘的三個解題套路

/% 看起來簡單,卻是 Level 0 出現頻率最高的解題工具。下面三個套路,之後的題目會一再用到。

套路 1:拆出一個數的每一位

n % 10 拿到個位數(除以 10 的餘數就是最後一位);n / 10把個位砍掉(整數除法把最後一位丟掉)。兩招交替,就能一位一位拆:

int n = 1234;
cout << n % 10 << endl;        // 4(個位)
cout << n / 10 % 10 << endl;   // 3(先砍掉個位變 123,再取個位 → 十位)
cout << n / 100 % 10 << endl;  // 2(先砍掉兩位變 12,再取個位 → 百位)

口訣:% 10 抓出尾巴那一位、/ 10 把尾巴砍掉。要一位一位處理,就 / 10 一直砍、每砍一次再 % 10 抓。

套路 2:1~N 中,d 的倍數有 N / d 個

先看例子:114 中,3 的倍數有 3, 6, 9, 12,共 4 個——正好是 14 / 3(整數除法)的結果。

為什麼?這些倍數依序是 1{\times}3,\ 2{\times}3,\ 3{\times}3,\ 4{\times}3——kd 的倍數就是 k \times d。「k \times d \le N」等價於「k 不超過 N / d 的整數部分」;再大一個(第 5 個是 15)就超過 14 了。所以:

1~N 中 d 的倍數,共有 N / d(C++ 整數除法,小數自動捨去)。

延伸一步:要數「被 4 7 整除」的個數,直接相加 N/4 + N/7 會把「同時被 47 整除」(也就是被 28 整除)的數算兩次,要再扣回來一次:

ans = N / 4 + N / 7 - N / 28;

這就是數學課學過的集合聯集公式(0.4 的學前數學清單有列):|A \cup B| = |A| + |B| - |A \cap B|

套路 3:無條件進位(向上取整)

整數除法是無條件捨去,但很多題目要的是無條件進位:遊戲 X 元、每天存 Y 元,至少要存幾天?X / Y 除不盡時,剩下的零頭得再多存一天。慣用寫法:

X 除以 Y 向上取整 = (X + Y - 1) / Y

為什麼「先加 Y - 1 再除」剛剛好?分兩種情況驗證:

  • X 整除 Y(餘數 0):多加的 Y-1 不足以再湊出一個 Y,商不變——本來就不用進位。
  • X 除以 Yr1 \le r \le Y-1):r + (Y-1) \ge Y,剛好把商往上推一格——這正是要的進位;又因為 r + (Y-1) < 2Y,最多只推一格、不會推過頭。

換你動手

學完五個算術運算子,最好的鞏固方式就是實際解題。去把本節下方的相關練習題做一遍——用 +-*/% 解決時間換算、糖分計算這類實際問題,寫完提交看看吧!