E2 进制与数据存储

对应大纲: 2.1.5-1(进制与进制转换)、2.1.5-5(ASCII 码)、2.1.2-4(位运算)、2.1.2-2(基本数据类型)

⚠️ 进制题出现在 19 套真题的 19 套里——一套不落。 这是初赛第一高频考点,本章必须吃透。

📝 S1_位运算与进制.md 讲的是复赛怎么用位运算;本章讲的是初赛怎么考。两份互补,都要看。


一、进制转换

权值表(背下来,考场上直接用)

位置 第4位 第3位 第2位 第1位
二进制 8 4 2 1
八进制 512 64 8 1
十六进制 4096 256 16 1

十六进制的字母A=10,B=11,C=12,D=13,E=14,F=15A{=}10,\ B{=}11,\ C{=}12,\ D{=}13,\ E{=}14,\ F{=}15

转十进制:按权展开

2708=2×64+7×8+0=18410112=8+0+2+1=11270_8 = 2\times64 + 7\times8 + 0 = 184 \qquad 1011_2 = 8+0+2+1 = 11

十进制转 R 进制:除 R 取余,倒着写

904÷16=568;56÷16=38;3÷16=0338816904 \div 16 = 56 \cdots 8;\quad 56 \div 16 = 3 \cdots 8;\quad 3 \div 16 = 0 \cdots 3 \Rightarrow 388_{16}

二 ↔︎ 八 ↔︎ 十六:直接分组,不用过十进制

1101 0110 1011  →  十六进制  D 6 B
110 101 101 011 →  八进制    6 5 5 3

📝 这就是十六进制流行的原因:一位十六进制正好等于四位二进制。

小数部分

小数点右边的权是 1R,1R2,\dfrac{1}{R}, \dfrac{1}{R^2}, \dots

101.112=4+1+12+14=5.7532.18=24+2+18=26.125101.11_2 = 4 + 1 + \frac12 + \frac14 = 5.75 \qquad 32.1_8 = 24 + 2 + \frac18 = 26.125

📝 背这三个:0.12=0.50.1_2 = 0.50.012=0.250.01_2 = 0.250.112=0.750.11_2 = 0.75


⚠️ 进制题的三条铁律

① 混进制运算,一律先全部转成十进制再算,最后再转回题目要的进制。

CSP 2024 第 2 题:(14810102)×D1611012(14_8 - 1010_2) \times D_{16} - 1101_2(1210)×1313=13(12 - 10) \times 13 - 13 = 13

② 选项进制各不相同时,把选项也全转成十进制再比。

CSP 2019 第 9 题:1010102+1668=42+118=160101010_2 + 166_8 = 42 + 118 = 160。 选项 (10110000)2=176(10110000)_2 = 176(236)8=158(236)_8 = 158(158)10=158(158)_{10} = 158(A0)16=𝟏𝟔𝟎(A0)_{16} = \mathbf{160}

③ 在原进制下直接做竖式加法,务必逐位算完再看选项。

CSP 2023 第 2 题:123456708+076543218=22222211812345670_8 + 07654321_8 = 22222211_8(不是 22222221822222221_8)——四个选项只差最后两位。


二、原码、反码、补码与整数范围

计算机里的负数用补码存储。

对 8 位为例,5-5

原码  1000 0101   (最高位是符号位,1 表示负)
反码  1111 1010   (符号位不变,其余取反)
补码  1111 1011   (反码 + 1)  ← 实际存的是这个

📝 正数的原码 = 反码 = 补码。 只有负数要变。

整数范围(必背)

类型 位数 范围
char 8 128127-128 \sim 127
short 16 3276832767-32768 \sim 32767
int 32 𝟐𝟏𝟒𝟕𝟒𝟖𝟑𝟔𝟒𝟖𝟐𝟏𝟒𝟕𝟒𝟖𝟑𝟔𝟒𝟕\mathbf{-2147483648 \sim 2147483647}
unsigned int 32 042949672950 \sim 4294967295
long long 64 ±9.2×1018\pm 9.2 \times 10^{18}

⚠️ 负数比正数多一个(因为 00 占了正数那侧的一个位置)。口诀:负的那头是整的(2147483648-2147483648),正的那头减一(21474836472147483647)。

📝 两个近似值背下来2312.1×1092^{31} \approx 2.1 \times 10^9int 上限)、2324.3×1092^{32} \approx 4.3 \times 10^9unsigned 上限)。

⚠️ 溢出int 存不下就会「绕回去」。2147483647+1=21474836482147483647 + 1 = -2147483648看到 a * ba,ba, b 都上万,先想会不会溢出。


三、ASCII 码

只背三个基准值,其余全靠加减:

’𝟶’=48,’𝙰’=65,’𝚊’=97\texttt{'0'} = 48, \qquad \texttt{'A'} = 65, \qquad \texttt{'a'} = 97

常用推论
'a' - 'A' 32(大小写差 32)
'9' 48+9=5748 + 9 = 57
'Z' 65+25=9065 + 25 = 90
'z' 97+25=12297 + 25 = 122
数字字符转数字 c - '0'
小写转大写 c - 'a' + 'A',或 c - 32

📝 ASCII 表的顺序:数字 < 大写字母 < 小写字母。 所以 'A' < 'a' 成立。

⚠️ (char)('a' + 13) 是什么? $97 + 13 = 110 = $ 'n'(CSP 2024 第 8 题)。用码值算,别数字母——数字母最容易差一。

⚠️ char 在常见平台上是有符号的(char)0xff1-1 不是 255255。CSP 2021 第 17 题就考了这个(table 初始化成 0xff,输出 int(table[0])1-1)。

⚠️ cout << 一个 char 打印的是字符不是数字。 要按数字打印必须写 cout << (int)c。CSP 2022 第 16 题就靠这个把「short 改成 char」变成了错误选项。


四、位运算

运算 符号 规则
& 两位都是 1 才得 1
| 有一个 1 就得 1
异或 ^ 不同得 1,相同得 0
取反 ~ 0 ↔︎ 1(含符号位)
左移 << x << k = x×2kx \times 2^k
右移 >> x >> k = x/2k\lfloor x / 2^k \rfloor

必背的三个套路

写法 作用 例子
x & (x - 1) 抹掉最低位的那个 1 255&254=254255 \,\&\, 254 = 254
x & (-x) 取出最低位的那个 1 12&(12)=412 \,\&\, (-12) = 4
x & 1 判奇偶(为 1 则奇数)

📝 由此推出两个高频结论

⚠️ CSP 2021 第 16 题整道题就是这两个套路f(x) 数 1 的个数、g(x) 取 lowbit。认出来这题就秒了。

异或的性质(考得多)

aa=0,a0=a,abb=aa \oplus a = 0, \qquad a \oplus 0 = a, \qquad a \oplus b \oplus b = a

📝 所以**「一堆数里只有一个出现奇数次,其余都出现偶数次」→ 全部异或起来就是答案。**


五、浮点数的坑

说明
不能用 == 比较 2×2\sqrt2 \times \sqrt2 不一定精确等于 22。要写 fabs(a-b) < 1e-9
精度 float 约 7 位有效数字,double 约 15 位。竞赛一律用 double
除法 int / int整除7 / 2 = 3。要小数必须 7.0 / 2
NaN 负数开根号得 nan,不是数字也不会报错

⚠️ CSP 2022 第 18 题(ans * ans == n) 这个浮点相等判断,nn 不是完全平方数时永远为 0,迭代再多次也没用。

⚠️ CSP 2023 第 16 题:输入 1 1 5 这种构不成三角形的边长,海伦公式里根号内是负数,实测输出 nan——所以「程序总是输出四位小数」是错的。

📝 浮点数的乘法交换顺序会改变二进制位(CSP 2023 第 16 题实测:11201\sim120 的全部三元组里有 142190 组的 double 结果不完全相同),但保留 4 位小数后看不出差别。这个细节值得记住。


30 秒自测

  1. 2708270_8 是十进制多少?904904 的十六进制是多少?
  2. 二进制转十六进制,从哪边开始、每几位一组?
  3. int 的范围是多少?为什么负数比正数多一个?
  4. '0''A''a' 的 ASCII 码各是多少?
  5. x & (x-1)x & (-x) 分别是干什么的?
  6. 怎么判断 xx 是不是 22 的幂?
  7. abba \oplus b \oplus b 等于什么?
  8. 为什么不能写 if (a == b) 比较两个 double
参考答案
  1. 2708=184270_8 = 184904=38816904 = 388_{16}
  2. 从右往左,每 4 位一组
  3. 21474836482147483647-2147483648 \sim 2147483647;因为 00 占用了正数一侧的一个编码
  4. 48 / 65 / 97
  5. 抹掉最低位的 1 / 取出最低位的 1
  6. x & (x-1) == 0(且 x>0x > 0
  7. aa
  8. 浮点运算有舍入误差,两个理论上相等的值二进制位可能不同;要用 fabs(a-b) < 1e-9