二进制、四进制、八进制、十六进制、三十二进制之间不需要「除基取余」 —— 每 m 位二进制正好是一位 2m 进制。十进制才需要真的除。这一页把这个「对齐」讲透,外加另外两个方向(十进制 ↔ 任意进制)的模板。
| 进制 | 等于 2 的几次方 | 每一位对应几位二进制 | 例子 |
|---|---|---|---|
| 二进制(2) | 21 | 1 位 | 1011 |
| 四进制(4) | 22 | 2 位 | 1011 → 10|11 = 23 |
| 八进制(8) | 23 | 3 位 | 101 011 → 538 |
| 十六进制(16) | 24 | 4 位 | 1010 1011 → AB |
| 三十二进制(32) | 25 | 5 位 | 0001 0101 → 15 |
| 十进制(10) | 不是 2 的幂 | — | 只能除基取余 |
关键在于「容量刚好对上」:
而 10 进制没有这个性质:一位十进制是 0~9(10 个值),二进制 3 位是 0~7、4 位是 0~15,都对不上。所以二进制和十进制之间必须真的做除法。
从右往左、每 4 位切一组,每组算出一个 0~15 的值,再换成对应的十六进制字符(0-9、A-F)。每组内部的权重是 8 / 4 / 2 / 1。
string binToHex(const string& s) { // 从右往左每 4 位一组
static const char* hex = "0123456789ABCDEF";
string t = s;
while (t.size() % 4) t = "0" + t; // 左端补零,凑成 4 的倍数
string out;
for (size_t i = 0; i < t.size(); i += 4) {
int v = 0;
for (int k = 0; k < 4; k++) v = v * 2 + (t[i + k] - '0'); // 逐位累加
out += hex[v];
}
size_t p = out.find_first_not_of('0');
return p == string::npos ? "0" : out.substr(p);
}
实测输出:
| 二进制 | 十六进制 | 说明 |
|---|---|---|
| 1010 | A | 10 → A |
| 10101010 | AA | 两组都是 A |
| 11010 | 1A | 补零成 0001 1010 |
| 11111111 | FF | 两组都是 15 |
如果题目只问「有多少个 A」(比如 PTA 那道 7-6),连十六进制字符都不用拼,直接数「有多少组 4 位等于 1010」:
for (int i = 0; i < len; i += 4) {
int v = (s[i]-'0')*8 + (s[i+1]-'0')*4 + (s[i+2]-'0')*2 + (s[i+3]-'0')*1;
if (v == 10) cnt++; // 10 就是 A
}
必须从右往左分组,因为进制对齐是「低位对齐」的。长度不是 4 的倍数时,要在左端补零。
另外记一个「白送」的性质:如果长度本来就是 4 的倍数(比如题目给的 4n 位二进制),那么从左往右切和从右往左切结果一样 —— 这时候前导零只是变成不产生影响的 0 组。反过来,长度不是 4 的倍数时一定不能从左切。
反过来更简单:每一位十六进制展开成固定的 4 位二进制,不足 4 位就在左边补零。
string hexToBin(const string& h) { // 每一位展开成 4 位
static const char* hex = "0123456789ABCDEF";
string out;
for (char c : h) {
int v = isdigit((unsigned char)c) ? c - '0' : toupper(c) - 'A' + 10;
for (int k = 3; k >= 0; k--) out += char('0' + ((v >> k) & 1));
}
size_t p = out.find_first_not_of('0');
return p == string::npos ? "0" : out.substr(p);
}
| 十六进制 | 二进制 |
|---|---|
| A5 | 10100101 |
| 1A | 11010 |
| F | 1111 |
只要有一边是十进制(或任何非 2 的幂进制),对齐就不成立,只能用这两种模板。两个都是 O(位数)。
long long toDec(const string& s, int base) { // v = v * base + d
long long v = 0;
for (char c : s) {
int d = isdigit((unsigned char)c) ? c - '0' : toupper(c) - 'A' + 10;
v = v * base + d;
}
return v;
}
实测:"1010"(2) = 10,"777"(8) = 511,"FF"(16) = 255。
string decToBase(long long x, int base) {
if (x == 0) return "0";
static const char* dig = "0123456789ABCDEF";
string out;
while (x) { out += dig[x % base]; x /= base; } // 先得到低位
reverse(out.begin(), out.end()); // 倒过来才是正常顺序
return out;
}
实测:255 → FF(16 进制)、511 → 777(8 进制)、10 → 1010(2 进制)。
c - '0';字母用 c - 'A' + 10(先 toupper 统一成大写,否则小写 a 会得到错误的值)。long long);再大就只能留在字符串里做加减乘。- '0'、字母 - 'A' + 10、大写先统一)?long long?
本页所有输出都在本机 g++ 16.2.0 下实际运行得到 · 返回资源列表