前言
这一章我们要搞清楚哪两个问题
- 数据如何在计算机中表示
- 运算器如何实现数据的算术、逻辑运算。
进位计数制
十进制
我们常说的几进制指的是权值,而真正的数是数码位。举个例子,十进制1234,那么我们要求他们的值就是数码位乘权值再相加。:
$$1234 = 1 \times 10^3 + 2 \times 10^2 + 3 \times 10^1 + 4 \times 10^0 $$
位权
我们把这种因为位置会发生的权值称为位权
r进制计数法
$$\begin{align*} \text{r 进制:}\ K_n K_{n-1} \dots K_2 K_1 K_0 K_{-1} K_{-2} \dots K_{-m} &= K_n \times r^n + K_{n-1} \times r^{n-1} + \dots + K_2 \times r^2 + K_1 \times r^1 + K_0 \times r^0 \\ &\quad + K_{-1} \times r^{-1} + K_{-2} \times r^{-2} + \dots + K_{-m} \times r^{-m} \end{align*} $$
基数:每个数码位所用到的不同符号的个数,r进制的基数是r。比如十进制的基数就是0,1,2,3,4,5,6,7,8,9
任意数转十进制
通过r进制计数法我们可以把任意r进制的数转换为十进制:
$$\text{二进制:} \ 101.1 \longrightarrow 1 \times 2^2 + 0 \times 2^1 + 1 \times 2^0 + 1 \times 2^{-1} = 5.5 $$
二进制与八/十六进制的相互转化
二进制转八进制
二进制是最适合用计算机来存储和处理的一种计数方式,原因如下:
- 只需要使用两个稳定状态的物理器件表示,比如高低电平和电容
- 0和1正好对应逻辑值的假和真,方便实现逻辑运算。
原数:$1111000010.01101_2$
转换规则:以小数点为界,整数部分从右往左、小数部分从左往右,每 3 位二进制为一组,不足 3 位时在两端补 0,再将每组二进制数转换为对应的八进制数字。
分组与补位
- 整数部分:
1111000010→ 从右往左分组,不足 3 位在最左侧补 0 →001 111 000 010 - 小数部分:
01101→ 从左往右分组,不足 3 位在最右侧补 0 →011 010 - 分组结果: $001\ 111\ 000\ 010\ .\ 011\ 010$
每组转换为八进制 - $001_2 = 1_8$
- $111_2 = 7_8$
- $000_2 = 0_8$
- $010_2 = 2_8$
- $011_2 = 3_8$
- $010_2 = 2_8$
最终结果:$$1111000010.01101_2 = 1702.32_8$$
二进制转十六进制
同二进制转8进制的分组与补位,只不过是4位分组
八进制与十六进制转二进制
反向分组与补位即可,如图

各种进制的常见书写方式
| 进制 | 标准括号表示法 | 后缀表示法 | 编程中常见前缀 / 后缀 |
|---|---|---|---|
| 二进制 | (1010001010010)2 | 1010001010010B |
末尾加 B,或前缀 0b |
| 八进制 | (1652)8 | — | 前缀 0(部分语言)或末尾加 O |
| 十六进制 | (1652)16 | 1652H |
前缀 0x 或末尾加 H |
| 十进制 | (1652)10 | 1652D |
末尾加 D(可省略,默认十进制) |
十进制转任意进制(整数部分)
核心原理: 对于十进制整数 $N$,转换为 $r$ 进制时,采用除基取余法:
- 反复用 (N) 除以基数 $r$
- 每次记录余数(作为 $r$ 进制的低位)
- 直到商为 0,最后将余数从下往上倒序排列,得到$(r$ 进制整数
通用公式
$$ \begin{align*} \text{r进制整数} &= K_n K_{n-1} \dots K_2 K_1 K_0 \\ &= K_n \times r^n + K_{n-1} \times r^{n-1} + \dots + K_2 \times r^2 + K_1 \times r^1 + K_0 \times r^0 \end{align*} $$
转换时:$$ \frac{K_n \times r^n + \dots + K_0 \times r^0}{r} = \underbrace{K_n \times r^{n-1} + \dots + K_1 \times r^0}_{\text{商}} \quad \underbrace{K_0}_{\text{余数(最低位)}} $$
示例:十进制转二进制(整数部分)
$(75_{10} \to ?_2$)
规则:$r=2$,除 2 取余,倒序排列
| 步骤 | 计算 | 商 | 余数 | 对应位 |
|---|---|---|---|---|
| 1 | 75÷2=37 | 37 | 1 | K0(低位) |
| 2 | 37÷2=18 | 18 | 1 | K1 |
| 3 | 18÷2=9 | 9 | 0 | K2 |
| 4 | 9÷2=4 | 4 | 1 | K3 |
| 5 | 4÷2=2 | 2 | 0 | K4 |
| 6 | 2÷2=1 | 1 | 0 | K5 |
| 7 | 1÷2=0 | 0 | 1 | K6(高位) |
| 余数倒序排列:K6K5K4K3K2K1K0=1001011 |
||||
| 最终结果: $$ (75)_{10} = (1001011)_2 $$ |
十进制小数转任意进制(小数部分)
核心原理: 对于十进制小数 $0.N$,转换为 $r$ 进制时,采用乘基取整法:
- 反复用小数部分乘以基数 $r$
- 每次记录整数部分(作为 $r$ 进制的高位)
- 剩下的小数部分继续乘 $r$,直到小数部分为 0 或达到指定精度
- 最后将整数部分从上往下顺序排列,得到 $r$ 进制小数
通用公式
-
$$\begin{align*} \text{r进制小数} &= 0.K_{-1}K_{-2}\dots K_{-m} \\ &= K_{-1} \times r^{-1} + K_{-2} \times r^{-2} + \dots + K_{-m} \times r^{-m} \end{align*} $$ 转换时: $$\underbrace{(K_{-1} \times r^{-1} + K_{-2} \times r^{-2} + \dots + K_{-m} \times r^{-m}) \times r} = \underbrace{K_{-1}}_{\text{整数(高位)}} + \underbrace{(K_{-2} \times r^{-1} + \dots + K_{-m} \times r^{-(m-1)})}_{\text{小数部分}} $$
| 步骤 | 计算 | 整数部分 | 剩余小数部分 | 对应位 |
|---|---|---|---|---|
| 1 | 0.3×2=0.6 | 0 | 0.6 | K−1(高位) |
| 2 | 0.6×2=1.2 | 1 | 0.2 | K−2 |
| 3 | 0.2×2=0.4 | 0 | 0.4 | K−3 |
| 4 | 0.4×2=0.8 | 0 | 0.8 | K−4 |
| 5 | 0.8×2=1.6 | 1 | 0.6 | K−5 |
| 6 | 0.6×2=1.2 | 1 | 0.2 | K−6 |
| … | … | … | … | … |
整数部分顺序排列:0.K−1K−2K−3K−4K−5K−6⋯=0.010011…
最终结果:
$$ (0.3)_{10} = (0.010011\dots)_2 $$
(注:十进制小数 (0.3) 转二进制是无限循环小数)真值和机器数
真值:符合人类习惯的数字
机器数:数字实际存到机器里的形式,正负号需要被“数字化”

定点数的表示
定点数 v.s. 浮点数
定点数:小数点的位置固定,比如996.007 ----常规计数法
浮点数:小数点的位置不固定,比如$9.96007*10^2$ ----科学计数法
二进制的定点数和浮点数也类似
无符号数的表示
无符号数:整个机器字长的全部二进制位均为数值位,没有符号位,相当于数的绝对值。
通常没有无符号的小数,只有无符号的整数。如果我们用unsigned修饰int或者long不会报错,但是修饰float是会报错的。
表示范围
8的二进制数:$2^8$种不同的状态0000 0000 ~ 1111 1111也就是0 ~ 255
n位的无符号数表示范围为:$0$ ~ $2^{n-1}$
有符号的定点表示

注意:
- 可用
原码,反码,补码三种方式来表示定点整数和定点小数。还可以用移码表示定点整数。 - 若真值为x,则用$[x]_原,[x]_反,[x]_补,[x]_移$分别表示真值所对应的原码,反码,补码,移码
原码
原码:用尾数表示真值的绝对值,符号位”0/1" 对应 “正/负”。若机器字长为n+1位,则尾数占n位。

常写作:$[x]_原 = 1,0010011$。
若未指明机器字长,也可以写作:$[x]_原 = 1,10011$
若机器字长n+1位,原码整数的表示范围为$-(2^n-1)<=x<=2^n-1$关于原点对称

常写作:$[x]_原 = 1.1100000$。
若机器字长n+1位,原码整数的表示范围:$-(2^n-1)<=x<=2^n-1$
若机器字长n+1位,原码小数的表示范围:$-(1-2^{-n})<=x<=1-2^{-n}$
原码的真值有+0和-0两种形式
反码
反码:
- 若符号位为0,则反码与原码相同
- 若符号位为1,则数值位全部取反

若机器字长n+1位,反码整数的表示范围:$-(2^n-1)<=x<=2^n-1$
若机器字长n+1位,反码小数的表示范围:$-(1-2^{-n})<=x<=1-2^{-n}$
真值0有+0和-0两种形式
其实反码只是原码转换为补码的一个中间状态,实际中没什么用
补码
补码:
- 正数的补码=原码
- 负数的补码=反码末位+1(要考虑进位)

补码的+0和-0的表示方式只有一种,那么接下来有个新的问题,对于定点整数$[-0]_原$=10000000的补码该是多少?我们可以知道补码是也是10000000,但是在定点整数补码$[x]_补=1,0000000$表示x=$-2^7$。
计算机里面一般只存补码
若机器字长n+1位,补码整数的表示范围:$-2^n<=2^n-1$(比原码多表示一个$-2^n$)
对于定点小数来讲,补码$[x]_补=1.0000000$,我们规定这个值为-1
若机器字长n+1位,补码小数的表示范围:$-1<=1-2^{-n}$(比原码多表示一个$-1$)
将负数补码转回原码的方法:尾数取反,末位加一
移码
移码:补码的基础上将符号位取反。注意:移码只能用于表示整数

若机器字长n+1位,移码整数的表示范围:$-2^n<=x<=2^n-1$(与补码相同)

(在不考虑符号的情况下)一移码从上到下真值增大,移码表示的整数很方便对比大小。常用语浮点数运算。
用几种码表示定点整数

这一页给出的只是整数,毕竟移码只能用于整数。
原码和反码的真值0有两种表达方式,补码和移码的真值0只有一种表示,补码和移码可以多表示一个负数。
各种码的作用
加减运算
使用原码运算:
- 加法:用加法器完成
- 减法:用减法器完成

我们把上面的时钟看成二进制的减法,下面的看成二进制的加法,我们可以通过这样的两种方式从10到7。在这里我们会说$-3\equiv9(mod 12)$,也就是在模12的情况下,-3与9等价。
模运算的性质
带余除法——设,$x,m\in{Z},m>0$则存在唯一决定的整数q和r,使得
$$x=qm+r,0<=r<m$$
数论中余数r的定义↑↑↑
也就是说(mod 12)把所有整数分为12类(余数为0~11),mod 12余数相同的数,都是同一类,都是等价的。即:$10+(-3),10+9,10+21...$在(mod 12)的条件下效果相同。并且在条件下同下,一对等价的数相加等于模,那么就称呼它们互为补数
模-a的绝对值 = a的补数
这个时候我们在(mod m)的条件下,若能找到负数的补数,就可以用正数的加法来等价替代减法。

这里要记住我们算出来的答案是补码形式。使用补码可以将减法操作转变为等价的加法,ALU中无需集成减法器。执行加法操作时,符号位一起参与运算。
把短数据变为长数据-零扩展&符号扩展

- ALU的位数是固定的,运算前可能需要把短数据扩展为长数据。
- 通用寄存器位数是固定的,把数据存入寄存器时,可能需要进行长度扩展
为什么寄存器里存的也是补码?
- 计算机的 ALU(算术逻辑单元)只会做加法,减法是通过「加负数的补码」实现的。
- 为了统一加减法逻辑,CPU 设计时就规定:所有有符号整数都用补码存储和运算。
- 寄存器和内存是平等的存储位置:数据从内存读到寄存器时,二进制位模式完全不变,补码是什么样,寄存器里就是什么样。
C语言中强制类型转换
C语言中的定点整数是用“补码”存储的
无符号与有符号

将有符号的x转换为无符号的y,由于short都是两字节,就会把补码原封不动的放过去,然后无符号下并不会把首位当作符号,所以原来的-4321变成了61215
长整型变短整型

当a = 0x000286a1时c = 0x86a1,真值-31071
当b = 0xffff7751时d = 0x7751,真值30545
短整型变长整型


对于一个定点整数的补码来说,如果它是一个负数,我们需要在高位,也就是符号位和原有的数值位之间添1,用这样的方式扩展得到的补码解析为真值的话,真值不变。

如果是无符号数之间的扩展,则需要添0
说些什么吧!