語法書 / AA 競程語法書 下冊 / 第十六單元 / 進制字串轉成整數

16.3 進制字串轉成整數

紙上會算了,換程式來。第一個方向:讀入一串 K 進制的數字,算出它的值

輸入是一串「位」,用 string 讀(10.7)最自然;每個字元要先變回數值——這正是 8.2 的看家本領:數字字元用 c - '0',字母位則是「跟 'A' 的距離再加 10」:

int charToDigit(char c) {
    if (c >= '0' && c <= '9') return c - '0';
    return c - 'A' + 10;      // 'A' 是 10、'B' 是 11……
}

這個函式假設字母是大寫。若題目給的是小寫 a ~ f,先用 8.5toupper 統一再送進來——直接對小寫做 - 'A' + 10 會得到錯得離譜的值('a' - 'A' + 1042,不是 10)。

方向一:由高位至低位(推薦)

想想你是怎麼「讀」出 3072 這個數的。逐字看到 3072,心裡的數字其實是這樣長大的:3 \to 30 \to 307 \to 3072——每看到新的一位,手上的數整體乘 10,再把新數字放進個位。把 10 換成 K,就是進制轉換的核心式:

\texttt{val} = \texttt{val} \times K + \texttt{digit}

(11010)_2 走一遍:

讀到的字元 運算 val 變成
'1' 0 \times 2 + 1 1
'1' 1 \times 2 + 1 3
'0' 3 \times 2 + 0 6
'1' 6 \times 2 + 1 13
'0' 13 \times 2 + 0 26

16.1 手算的 26 一致。完整程式(輸入進制 K 與 K 進制字串,輸出十進制值):

#include <iostream>
#include <string>
using namespace std;

// 進制位字元 -> 數值:'0'~'9' 用 8.2 的減法,'A' 起跳的字母代表 10 以上
int charToDigit(char c) {
    if (c >= '0' && c <= '9') return c - '0';
    return c - 'A' + 10;
}

int main() {
    int K;
    string s;
    cin >> K >> s;                          // K 進制的字串 s

    long long val = 0;                      // 數值上看 10^18:用 long long
    for (int i = 0; i < (int)s.size(); i++) {
        val = val * K + charToDigit(s[i]);  // 舊值整體升一位,個位放新數字
    }
    cout << val << '\n';
    return 0;
}

執行結果(輸入 161A):

26

(1A)_{16} = 1 \times 16 + 10 = 26——換輸入 211010 跑跑看,同樣得到 26。注意 val 宣告成 long long:題目的數值動輒上看 10^{18},用 int 一路乘上去必溢位(上冊 2.9)。

方向二:由低位至高位

也可以照 16.1 手算的方式:從最右邊(低位)開始,每一位乘上自己的位值 K^i 再加總。位值用一個變數 mul 一路乘上去:

#include <iostream>
#include <string>
using namespace std;

int charToDigit(char c) {
    if (c >= '0' && c <= '9') return c - '0';
    return c - 'A' + 10;
}

int main() {
    int K;
    string s;
    cin >> K >> s;

    long long val = 0;
    long long mul = 1;                        // mul = 目前這一位的位值 K^i
    for (int i = (int)s.size() - 1; i >= 0; i--) {
        val += mul * charToDigit(s[i]);       // 這一位的貢獻:數字 × 位值
        if (i > 0) mul *= K;                  // 位值升一級(最後一次不用,避免多乘一次爆範圍)
    }
    cout << val << '\n';
    return 0;
}

輸出跟方向一完全相同。多解釋一下倒數第二行的 if (i > 0):最高位處理完後,mul 不會再被用到,但若放任它多乘最後一次 K,在數值貼近 long long 上限時(例如 19 位的十進制輸入),這次多餘的乘法自己就先溢位了(2.9)——一個 if 把隱患拆掉。

兩種方向都對,推薦方向一:不用管位值變數、迴圈順向好寫,而且它需要的「由高位往低位逐字掃」恰好就是字串本來的順序。

動手試試看:先手算 36 進制的 AA 是多少(A10,所以是 10 \times 36 + 10 = 370),再拿本節任一版程式驗證;順便輸入 2 與你在 16.1 算過的 101101,看看是不是 45