本章问题:
- 数据如何在计算机中表示?
- 运算器如何实现数据的算术、逻辑运算?
2.1.1 进位计数制
十进制计数法
- 古印度人发明阿拉伯数字。
- 基于乘法思想。
- 十进制表示方式:
整数情况下:
带小数的情况:
\[K_nK_{n-1}...K_2K_1K_0K_{-1}K_{-2}...K_{-m} = K_n \times 10^n + K_{n-1} \times 10^{n-1} + ... + K_2 \times 10^2 + K_1 \times 10^1 + K_0 \times 10^0 + K_{-1} \times 10^{-1} K_{-2} \times 10^{-2} + ... + K_{-m} \times 10^{-m} \]- 上面的公式中\(10^n\)被称为位权。
推广:r进制计数法
- r进制转十进制公式:
- 基数:每个数码位所用到的不同符号的个数,r进制的基数为r。
- 计算机中常用进制:
- 二进制:0,1
- 八进制:0,1,2,3,4,5,6,7
- 十进制:0,1,2,3,4,5,6,7,8,9
- 十六进制:0,1,2,3,4,5,6,7,8,9,A, B, C, D, E, F
- 二进制适合计算机的原因:
- 可使用两个稳定状态的物理器件表示。
- 0,1正好对应逻辑值假、真。方便实现逻辑运算。
- 可以很方便的使用逻辑门电路实现算术运算。
各种进制的常见书写方式
- 二进制:
\((1010000101010)_2 = 010000101010B\) - 八进制:
\((1652)_8\) - 十六进制:
\((165A)_{16} = 165AH = 0x165A\) - 十进制:
\((160)_{10} = 160D\)
十进制转其他进制
例如:十进制75.3转r进制:
- 整数部分(除基取余法):
\(\frac {75}{r} = 商(X)…余数(K_0)\)
\(\frac {X}{r} = 商(X)…余数(K_1)\)
……
\(\frac {X}{r} = 商(0)…余数(K_n)\)
r进制整数部分为:
\(K_nK_{n-1}...K_2K_1K_0\) - 小数部分(乘积取整发):
\(0.3 \times r = 整数(K_{-1}) + 小数(X)\)
\(X \times r = 整数(K_{-2}) + 小数(X)\)
……
r进制小数部分为:
\(K_{-1}K_{-2}...\) - 注:十进制的小数部分可能无法用其他进制的有限小数精确表示,可能出现循环。
- 如果熟悉二进制的每位对应的十进制数,在求十进制转八、十六进制时可以先十进制转二进制,然后二进制转八、十六进制。
真值和机器数
- 如果数字带符号,存储时需要一位符号位。
- 真值:符合人类习惯的数字。
- 机器数:数字实际存到机器里的形式,它的正负号需要被“数字化”。
2.1.2 BCD码
- BCD(Binary-Coded Decimal): 使用二进制编码的十进制。
- 二进制方便机器处理,十进制符合人类习惯,但是用二进制转十进制的公式转换的过程较为复杂,所以引出BCD编码。BCD码的思路是用4b二进制表示1为十进制。4b二进制可以表示16个数,对应十进制有6个数的冗余,但是转换起来比较方便。
8421码(重点)
- 8421码的4位二进制对应十进制分别位8421,也就是一般状况下二进制和十进制的对应关系。
- 下表给出8421码和十进制的映射关系:
十进制数 | 8421码 |
---|---|
0 | 0000 |
1 | 0001 |
2 | 0010 |
3 | 0011 |
4 | 0100 |
5 | 0101 |
6 | 0110 |
7 | 0111 |
8 | 1000 |
9 | 1001 |
- 例:985用9421码表示为:1001 1000 0101
- 8421码如何进行加法运算?
投机取巧法: 先用十进制加出结果,然后把结果用8421码表示。- 计算机方法:
例如十进制5 + 8,对应二进制8421码为0101 + 1000 = 1101. 但是1101不在8421码的映射表里。计算机的处理方式为当计算结果出现超出映射表的数,就给这些数加六(0110)(因为8421码有6位冗余),这样这个数就会向高位进一,剩下的部分就是低位的结果。例如1101 + 0110 = 1 0011,对应8421码的13. - 例: 9 + 9的9421码运算过程:
解:1001 + 1001 = 10010
10010超出映射表,修正为10010 + 0110 = 11000 = 18
余3码
- 映射规则: \(8421码 + (0011)_2\)
- 十进制映射表如下:
十进制数 | 余3码 |
---|---|
0 | 0011 |
1 | 0100 |
2 | 0101 |
3 | 0110 |
4 | 0111 |
5 | 1000 |
6 | 1001 |
7 | 1010 |
8 | 1011 |
9 | 1100 |
- 8421码每位的权值是固定的,称为有权码,而余3码每位权值不固定,称为无权码。
2421码
- 每位权值分别为2421.
- 十进制映射表如下:
十进制数 | 2421码 |
---|---|
0 | 0000 |
1 | 0001 |
2 | 0010 |
3 | 0011 |
4 | 0100 |
5 | 1011 |
6 | 1100 |
7 | 1101 |
8 | 1110 |
9 | 1111 |
- 2421码的04首位都是0,59首位都是1,这样可以使2421码编码策略唯一。
2.1.3 无符号整数的表示和运算
- 无符号整数,即自然数:0,1,2,3,4……
- C语言中的无符号整数:
unsigned short a = 1; //无符号整数(短整型,2B)
unsigned int b = 2; //无符号整数(整型,4B)
无符号整数在计算机硬件内如何表示?
- 计算机硬件能支持的无符号整数位数有上限,现在个人计算机机器字长通常是64位,或至少为32位。
本节以8位机器字长为例: - 下图为计算机存储无符号数的示意图:
由图可知,如果要把一个超出机器字长表示范围的数强行塞进一个机器字长内,计算机只保留低8位,其余高位部分溢出。 - 全部二进制都是数值为,没有符号位。
- n bit无符号整数的表示范围位\(0\)~\(2^{n-1}\),超出范围就会溢出。
- 可以表示的最小的数全是0,最大的数全是1.
无符号整数的加减法运算如何用硬件实现?
-
无符号整数加法:从最低位开始,按位相加,并往最高位进位。
例:99 + 9
解:01100011 + 00001001 = 01101100 -
无符号整数减法:
人类方法:从最低位开始,按位相减,不够减的话从高位借位。
计算机方法:- “被减数”不变,减数全部按位取反,末位+1,减法变加法。
- 从最低位开始,按位相加,并往最高位进位。
- 原因是加法电路造假便宜,减法电路更贵。
例:99 - 9
解:01100011 - 00001001
= 01100011 + (11110110 + 1)
= 01100011 + 11110111
= 1 01011010
最高位的1溢出,最终结果为01011010
2.1.4 带符号整数的表示和运算
原码
- 最高位表示正负号,其余位表示数值位。
- 原码表示图如下:
符号位0/1对应正/负,剩余数值位表示真值的绝对值。 - 若机器字长为n-1位,带符号整数的原码表示范围:\(-(2^n - 1) \leq x \leq 2^n-1\)
- 真值0的原码表示方式有两种,一种是+0,一种是-0.
- 常见书写方法:\([-19]_原 = 1,0010011\),符号位和数值位中间可以用逗号隔开。如果题目没说机器字长,可以省略数值位开头的0.
- 原码缺点:符号位不能参与运算。
补码、反码
- 原码到反码补码的转换:
- 正数原、反、补码相同。
- 复数原码到反码符号位不变,数值位按位取反,反码到补码末位加一。
- 原、反、补的最高位都反应正负。
- 原->反和反->原都是符号位不变,数值位按位取反。反码到补码末位加一,补->反末位减一。
- 原<->补的快速转换:
正数不变,负数从右往左找到第一个1,这个1左边的所有数值位按位取反,原->补和补->原都一样。 - 补码的加减运算:
- 补码的加法运算:
+19 + (-19) = 00010011 + 11101101 = 1,00000000
最前面的1溢出不考虑,最终结果为0. - 补码的减法运算:
因为加法电路比减法电路便宜,所以尽量把减法运算变成等价的加法运算。
\([A]_补 - [B]_补 = [A]_补 + [-B]_补\)
\([B]_补\)<->\([-B]_补\)全部位按位取反、末位+1. 也可以从右往左找到第一个1,这个1左边所有位(包括符号位)都取反。
无符号整数的减法也是被减数不变,减数全部位按位取反,末位+1,减法变加法。
也就是同一套电路可以实现无符号和有符号整数的减法。
- 补码的加法运算:
各种码的基本特性总结:
- 注:是否溢出的计算可以手算带十进制验证。
移码
- 原、反、补、移码的转换:
- 补码的基础上符号位取反可得移码。
- 移码的正数表示和其他三个码不同。
- 移码只用于表示整数。原、反、补码也可以表示小数。
- 移码整数的表示范围和补码相同,机器字长为n+1位时,表示范围都是:
\(-2^n \leq x \leq 2^n-1\) - 移码补码对应图:
在-128~127之内,移码的真值从小到大增加。
2.1.5 定点小数
- 定点含义:小数点的位置固定。带符号整数也可以叫定点整数,默认小数点固定在最后一位之后。
- 定点小数的小数点位置默认在符号位之后。
原码
- 定点整数的符号位与数值位通常用逗号隔开,定点小数的符号位和数值位通常用“.”隔开。
反码、补码
- 和定点整数的转换方式相同:
正数不变。
负数:
原<->反:符号位不变,数值位按位取反;
原<->补:从右往左找第一个1,这个1左边的所有数值位按位取反。
反->补:负数末位+1.
定点小数的加减运算
运算规则:
和定点整数的加减运算规则相同。
定点小数和定点整数的总结:
- 定点小数的默认小数点在符号位之后,定点整数默认小数点在数值位之后。
- 定点小数和定点整数对照表如下:
- 定点小数和定点整数在位数扩展时,拓展位置不一样。
定点小数位数扩展是在数值位末位加0.
定点整数位数扩展是在符号位后面加0.
2.2.0 奇偶校验码(大纲已删)
校验原理
如果PCA和PCB要传输4钟信号ABCD,那么理论上只需要2b的数据来表示这4个合法状态。如下表所示
信息 | A | B | C | D |
---|---|---|---|---|
编码 | 00 | 01 | 10 | 11 |
但是在数据传输过程中,可能会出现位错误,所以可以采用校验的方式,用3b来编码这4个合法状态(会有4中冗余的非法状态),编码方式如下表:
信息 | A | B | C | D |
---|---|---|---|---|
编码 | 100 | 001 | 010 | 111 |
表中的4中编码为合法状态,另外4中为冗余非法状态。 |
奇偶校验
- 奇偶校验码:
- 检验错误的方式:
一串带着校验码的数据传输过去,约定好是奇校验或者偶校验,接收方先检查1的奇偶性,如果传输过程中发生奇数bit的数据错误,则接收方可以测出来,但是发生偶数bit的数据错误,接收方就检查不出来了。 - 偶校验的硬件实现:各信息进行异或(模2加)运算,得到的结果极为偶校验位。
2.2.1 算术逻辑单元
作用、大致原理
- ALU(Arithmetic and Logic Unit):用于实现算术运算、逻辑运算和一些辅助功能(移位、求补)等。
- ALU有A、B两个输入信号,一个F输出信号,一些控制输入信号。
- 具体实例:
74181芯片是一个4位ALU,右侧的输入就是控制信号,用来控制当前的运算。
电路基础知识
- 基本逻辑运算:
当一个逻辑运算中同时出现&和|,先算与,再算或。 - 复合逻辑:
与非:A和B先与再非
或非:A和B先或再非
异或:可以用与、或符合而来,A、B只有一个为1是结果为1
同或:异或取反
加法器的实现
一位全加器
- 本位有两个参与运算的数\(A_i\)和\(B_i\),还有低位进位\(C_{i-1}\), 本位的和记为\(S_i\)
- 加法是一位一位的加,本位的输出结果&S_i&:输入中有奇数个1.
向高位的进位\(C_i\)是输入中至少有2个1.
\[C_i = A_iB_i + (A_i \oplus B_i)C_{i-1} \]其中\(A_iB_i\)表示两个本位都是1, +表示或, \((A_i \oplus B_i)C_{i-1}\)表示两个本位中有一个1,低位进位为1.
- 一位全加器英文缩写为FA(Full Adder),图示如下:
- 如果利用一位全加器实现多位加法?
- 串行全加器:
在一位全加器的基础上增加一个进位触发器,用来保存进位位。
整个运算过程只有一个一位全加器,数据逐位串行送人加法器中进行运算。进位触发器用来寄存进位信号,以便参与下一次运算。
串行加法器的图示如下:
缺点:效率较低。 - 串行进位的并行加法器:
把n个全加器串接起来,即可进行n位相加。
进位信息是串行的一位一位往前进的。
串行进位也叫行波进位。 - 并行进位的并行加法器:
串行进位的加法器计算速度受进位信号产生速度的影响。并行进位的并行加法器解决的问题是如何让进位计算的更快。
进位信号的计算可以有如下递推公式:
一直向下递推最终可以推到\(C_0\)
通过对逻辑表达式的化简,最终可以用\(C_0\)和\(A_iA_{i-1}...A_1\), \(B_iB_{i-1}...B_1\)来表达\(C_i\)
由此设计出的电路每一位的进位几乎是同时产生的,提高了效率。但是每递推一次,表达式越复杂所以一般推到\(C_4\),每4位之间串行,4位之内并行。
- 串行全加器:
2.2.2 补码加减运算器
- n bit补码X + Y,直接相加即可。
- n bit补码X - Y,将补码Y全部按位取反,末位+1,得到\([-Y]_补\),减法变加法。
- 补码的加减运算器:
Y是通过一个多路选择器连接的ALU,在控制信号为0(加法)时,Y直接输入到ALU中,低位进位与多路选择器的控制信号相连,数值为0。当多路选择器的控制信号为1(减法)时,Y取反输入ALU,\(C_{in}\)值为1,相当于X + (-Y)(Y按位取反) + 1。
无符号数的加减运算也可以用同样的电路。
加减运算和溢出判断
原码的加法运算
- 正 + 正:绝对值做加法,结果为正。
- 负 + 负:绝对值做加法,结果为负。
- 正 + 负:绝对值大的数减绝对值小的数,结果符合与绝对值大的保持一致。
- 注:加数和被加数的符号相同时,结果可能出现溢出。
原码的减法运算
核心思路是转变为加法运算。
- 正 - 正 = 正 + 负
- 正 - 负 = 正 + 正
- 负 - 正 = 负 + 负
- 负 - 负 = 负 + 正
溢出判断
-
溢出分为上溢和下溢,如下图所示:
当“正 + 正”超出正数区表示的上限时,发生上溢,上溢的结果是“正 + 正 = 负”
当“负 + 负”小于负数区表示的下限时,发生下溢,下溢的结果是“负 + 负 = 正” -
下图所示为补码加法的另一种理解:
上图所示位3位补码所能表示的8个数,一个数加另一个正数X,可以理解为向右移动X个数。例如2 + 2,即从2向右移两位,超出部分绕到最左边。2向右移一位是3,再移是-4,所以3位补码2 + 2计算结果的真值为-4,发生溢出。 -
溢出判断方法一:
\( V = A_sB_s \overline{S_s} + \overline{A_s} \overline{B_s}S_s \)
解释:\(A_s\)、\(B_s\)表示两个参与运算数字的符号位。\(S_s\)表示结果的符号位。该逻辑表达式的含义为A、B为正结果为负,或A、B为负,结果为正。
V = 0表示无溢出;
V = 1表示有溢出。
写逻辑表达式的作用是未来便于硬件电路的实现。 -
溢出判断方法二
根据数据位进位情况判断溢出。设符号位的进位为\(C_s\),最高数值位的进位为\(C_1\).
最高数值位的进位指数值为计算时算到最高位有没有向前进位。
符号位的进位即两个符号位同时为1时,相加会出现进位1,否则没有进位。
溢出情况如下表:
\(C_s\) | \(C_1\) | |
---|---|---|
上溢 | 0 | 1 |
下溢 | 1 | 0 |
即\(C_s\)和\(C_1\)不同时发生溢出。
解释一下上面表格:当符号位进位为零可能有3种情况:正 + 正、正 + 负、负 + 正。其中只有正 + 正可能出现溢出,当正 + 正的数值位有进位时,整体发生溢出(上溢);当符号位的进位为1时,说明是负 + 负,负 + 负的情况下符号位相加结果为0,向高位进1,若数值位有进位,则进的这一位将写在符号位上,符号位为1,得到“负 + 负 = 负”的合法结果。但是如果数值位没有进位,则符号位为0,出现“负 + 负 = 正”的下溢情况。
逻辑表达式如下:
\(V = C_s \oplus C_1\)
V = 0, 无溢出
V = 1,溢出
- 溢出判断方法三
采用双符号位,正数符号00,负数符号11,计算结果符号位为01时上溢,计算结果符号位为10时下溢。
这个方法的原理和方法二是一样的。
另一种解释:双符号为的第一位表示本应得到的正负情况,第二位表示实际得到的正负情况,01是结果本应是正的,实际是负的,判断为上溢。10反之。
双符号位的补码又称:模4补码
单符号位的补码又称:模2补码
双符号位在计算机中的实际存储时也只存一个符号位,在运算时会复制一个符号位。
符号扩展
- 正整数的扩展原、反、补都是一样的,直接在前面加0.
- 负整数的扩展如下表:
扩展方式 | 负整数 | 扩展结果 | |
---|---|---|---|
原码 | 符号位后加0 | 1,1011010 | 1,00000000 1011010 |
反码 | 符号位后加1 | 1,0100101 | 1,11111111 0100101 |
补码 | 符号位后加1 | 1,0100110 | 1,11111111 0100110 |
- 正小数的符号扩展原、反、补都是一样,在小数末尾加0.
- 负小数的符号扩展如下表:
扩展方式 | 负小数 | 扩展结果 | |
---|---|---|---|
原码 | 末尾填0 | 1.1011010 | 1.1011010 00000000 |
反码 | 末尾填1 | 1.0100101 | 1.0100101 11111111 |
补码 | 末尾填0 | 1.0100110 | 1.0100110 00000000 |
标志位的生成
加法器除了计算相加结果之外,还可以生成4个标志位。
OF(Overflow Flag)
- 溢出标志。溢出时为1,否则为0.
- 仅在有符号数的加减运算中有意义。对无符号数无意义。
- 硬件计算方法:\(OF = 最高位产生的进位 \oplus 次高位产生的进位\)
SF(Sign Flag)
- 符号标志。结果为负时为1,否则为0.
- 硬件计算方法:\(SF = 最高位的本位和\)
- SF对无符号加减计算无意义。
ZF(Zero Flag)
- 零标志。运算结果为0时为1,否则为0.
- 硬件计算方法:两个数的运算结果有n位,只有n位全为0,ZF = 1.
CF(Carry Flag)
- 进位/借位标志。进位/借位时置1,否则为0.
- 仅对无符号数加减法有意义。
- 硬件计算方法:
\(CF = 最高位产生的进位 \oplus sub, sub = \begin{cases} 0,表示加法\\ 1,表示减法 \end{cases}\) - 无符号数借位的含义是两个数计算结果用补码来读真值是负的,但是无符号数不会有负的,所以解释为借位。
2.2.3 定点数的移位运算
移位:通过改变各个数码位和小数点的相对位置,从而改变各数码位的位权。可以用移位运算实现乘法、除法。
算术移位(常考)
原码的算术移位
符号位保持不变,仅对数值位进行移位。
相当于乘或除\(2^n\)
- 右移:符号位不动,高位补0,低位舍弃。若舍弃的位是0则相当于除2;若舍弃的不是0,则会丢失精度。
- 左移:符号位不动,最高位舍弃,低位补0。若舍弃的位 = 0,则相当于乘2;若舍弃的位不等于0,则会出现严重误差。
反码的算术移位
- 正数和原码一样。
- 负数的反码算术移位:
右移:符号位不动,高位补1,低位舍弃。
左移:符号位不动,低位补1,高位舍弃。
补码的算术移位
- 正数跟原码一样。
- 负数的补码算术移位:
右移:符号位不动,高位补1,低位舍弃。
左移:符号位不动,低位补0,高位舍弃。
算术移位的应用
例:求-20 * 7
解:
\((-20) \times 7 = (-20) \times (2^2 + 2^1 + 2^0)\)
也就是(-20) + (-20)算术左移1位 + (-20)算术左移2位。
逻辑移位
- 逻辑右移:高位补0,低位舍弃。
- 逻辑左移:低位补0,高位舍弃。
- 可以看作是对无符号数的算术移位。
循环移位
把移出去的为补到另一头。
- 带进位位的循环移位:
进位位也参与移动,用来记录数据进位。相当于给原本的数据多添了一位。 - 适用于把数据的高位和低位进行高低字节的调换。
2.2.4 乘法运算
乘法运算的实现思想
手算二进制的乘法和手算十进制的乘法过程相同。
考虑用机器实现,会有一些问题:
- 实际数字有正有负,符号如何处理?
- 乘积的位数要扩大一倍,如果处理?
- 每位的位积都要保存下来最后统一相加吗?
原码的一位乘法
- 符号位单独处理:\(符号位 = X_s \oplus Y_s\)
- 数值位取绝对值进行乘法计算。
- 乘法运算实现方法:先加法再移位,重复n次。
在正式进行乘法之前,ACC置0,乘数存在MQ内,被乘数存在X中,当乘数最低为位1时,(ACC + X) -> ACC;当乘数最低位为0,ACC不加X。之后ACC和MQ整体逻辑右移一位,重复之前的过程。
上图中红色的部分可以称为部分积。
定点小数的小数点隐藏在符号位后面,计算的时候可以不用考虑。
最终计算结果ACC中保留的是乘积高位,MQ中保留乘积低位。 - 原码一位乘法(手算模拟):
上述过程中,被乘数和乘数采用双符号位,但是实际做题的时候采用单符号位也可以。 - 本节中采用小数作为例子,整数的原理相同。
补码的一位乘法
- 补码一位乘法和原码一位乘法的过程相似,区别是:
- 补码需要进行n轮加法、位移,最后比原码再多一次加法。
- 原码每次加法可能加0或\(+[|X|]_原\),补码依据MQ中的最低位和一个辅助位判断,每次可能加0、\(+[X]_补\)或\(+[-X]_补\)。
- 辅助位 - MQ中的最低位 = 1时,\([ACC] + [X]_补\)
- 辅助位 - MQ中的最低位 = 0时,\([ACC] + 0\)
- 辅助位 - MQ中的最低位 = -1时,\([ACC] + [-X]_补\)
- 原码每次逻辑右移,补码每次算术右移。
- 原码的乘数符号位不参与运算,补码乘数符号位参与运算。
- 硬件构成:
MQ中的实际最低位为新增的辅助位,由于MQ增加了一位,其余寄存器(ACC, X)也要随之增加一位(被乘数采用双符号位的补码,乘数采用单符号位的补码)。
可能会有一个辅助电路实现\([X]_补\)到\([-X]_补\)的转换。 - 采用Booth算法求\(X \times Y\):
2.2.5 除法运算
除法运算的思想
手算二进制除法:
例:设机器字长为5为(含一位符号位,n = 4),x = 0.1011, y = 0.1101, 求x/y:
解:可以先把除数和被除数都乘\(2^4\),变成正数。
原码除法:恢复余数法
- 运算器的结构:
- 实现方法:上商0/1,得到余数,余数末尾补0.
- 符号位单独处理:\(符号位 = X_s \oplus Y_s\)
数值位取绝对值进行除法运算。 - ACC中存被除数,通用寄存器(X)中存除数的值。MQ中存最后的商,一开始全部取0. MQ的最后一位是当前要确定的商的位置。
- 每位的商计算机都会先默认为1,ACC里的被除数加除数的相反数的结果存到ACC中去。ACC的结果如果是正的,则这一位就是商1。ACC的结果如果是负的(符号位为1),说明这一位商不该是1,就把MQ末位的值改为0,ACC里的数再加除数。之后再把ACC和MQ里的所有值统一逻辑左移一位。
- 如果最后一位商1后发现需要恢复余数,则还需把最后一位商改成1,再把ACC里的值加上除数。
- 得到的余数还要再乘\(2^{-n}\),才是最终余数的值。
- 注:计算过程中如果出现要加(-Y),加的都是补码。
原码除法:加减交替法(不恢复余数法)
- 由于恢复余数发的过程较为麻烦,所以可以做一些优化,改为不恢复余数的除法。原理就是恢复余数法恢复余数后,商0,还要逻辑左移1位,再减去除数。这个过程简化就可以直接变成商0,不恢复余数,左移后加上被除数的补码。
- 符号位的确定也需要单独解决:\(Q_s = X_s \oplus Y_s\)
- 最终余数的正负性和商相同。
- 加、减执行n + 1次,每加一次确定一位商;左移n次(最后一次加减完不移位),最终可能还要多加一次。
- 最后说明:这里讨论的是定点小数的除法,最终结果也是定点小数。所以被除数应该要小于除数,否则结果会大于1,而定点小数无法表示大于1的范围。原码除法通过第一位的商来判断被除和除数的大小。
补码除法:加减交替法
- 补码除法:符号位参与运算,被除数/余数、除数采用双符号位。
- 在写补码的时候,写的是除数的补码,而不是除数绝对值的补码。因为除数的符号位直接参与运算。
- 第一步:判断被除数和除数是否同号,同好则被除数减去除数,异号则被除数加除数,得到一个新的余数。
- 如果除数和余数同号,商1,余数左移一位减去除数。
如果余数和除数异号,商0,余数左移一位加除数。
如此重复n次。
最后一位的商恒为1,不管同号异号(主要是为了省事)。
除法运算的总结:
除法类型 | 符号位是否参与运算 | 加减次数 | 移位方向 | 移位次数 | 上商、加数原则 | 说明 |
---|---|---|---|---|---|---|
原码加减交替法 | 否 | N+1或N+2 | 左 | N | 余数的正负 | 若最终余数为负,需要恢复余数 |
补码加减交替法 | 是 | N+1 | 左 | N | 余数和除数是否同号 | 商末位恒置1 |
2.2.6 C语言类型转换
C语言中的定点整数都是用补码的形式存储的
void main(){
short x = -4321;
unsigned short y = (unsigned short)x;
int a = 165537, b = -34991;
short c = (short)a, d = (short)b;
// short x = -4321;
int m = x;
unsigned short n = (unsigned short)x;
unsigned int p = n;
}
- 在上述代码中无符号数与有符号数的转变不改变数据内容,只改变解释方式。
x = -4321 = \((1,110\space 1111\space 0001\space 1111)_2\).
y = \((1110\space 1111\space 0001\space 1111)_2\). = 61215. - 长整数转短整数:高位截断,保留低位。
a = 0x0002 86a1
c = 0x86a1 真值-31071
b = 0xffff 7751
d = 0x7751 真值30545 - 更短的数据转为更长的数据:符号扩展。
x = 0xef1f
m = 0xffff ef1f 真值-4321.
n = 0xef1f 真值61215
p = 0x0000 ef1f 真值61215
2.2.7 数据的存储和排列
大小端模式
对于一个4字节的int:
19088743D
01 23 45 67H
0000 0001 0010 0011 0100 0101 0110 0111B
- 其中,十六进制的01为这个数的最高有效字节(MSB)。67为最低有效字节(LSB)。
- 多字节的数据在内存中一定是占连续的几个字节的。
一般有大端、小端两种存储方式。
其中大端方式便于人类阅读,每个字节按照地址从低到高存储。低地址存储最低有效字节,高地址存最高有效字节。
小端方式逆过来,最高有效字节在低地址,最低有效字节在高地址。
边界对齐
现代计算机通常是按字节编址,即每个字节对应1个地址
通常也支持按字、按半字、按字节寻址。
假设存储字长为32位,则1个字 = 32bit,半字 = 16bit。每次访存只能读/写1个字。
边界对齐的存储方式和边界不对齐的存储方式如下图:
边界对齐读取方便但是有点浪费空间,边界不对齐方式节约空间,但是读取过程比较浪费时间。
2.3.1 浮点数的表示
- 科学计数法的表示方式:
阶符负责标记小数点移动端方向,阶符为正,小数点后移,阶符为负,小数点前移。
阶符的数值部分表明小数点要移动多少位。
尾数数符表示整个数值的正负。
尾数的数值部分位数越小精度越小。
浮点数的表示
- 阶码(E):常用补码或移码表示定点整数。反应浮点数的表示范围及小数点的实际位置。
- 尾数(M):常用原码或补码表示的定点小数。数值部分的位数N反应浮点数的精度。
- 二进制浮点数的真值:\(N = r^E \times M\),其中r为阶码的底,通常是2,也可以取\(2^i\).
例:阶码、尾数都用补码表示,求a, b的真值。
a = 0,01;1.1001
b = 0,10;0.1001
解:a的尾数的原码表示对应的二进制真值为:-0.0111,a的真值 = \(2^1 \times (-0.0111) = -0.111\)
b的阶码010对应真值为+2.尾数0.01001对应真值+0.01001,b的真值 = \(2^2 \times (+0.01001) = +1.001\)
注:a有3位阶码和5位尾数,在1B的存储空间里可以刚好存下;但是b有3位阶码和6位尾数,在1B空间内存不下,会使精度降低。如何能在有限的空间内尽可能高的提高浮点数的精度呢?这需要进行浮点数的规格化。
浮点数尾数的规格化
在上面例子中,b = \(2^2 \times (+0.01001) = 2^1 \times (+0.1001)\)
即将尾数算术左移一位,阶码-1,这个过程称为左归。
同理,右归是当浮点数运算结果尾数出现溢出(双符号位为01或10)时,将尾数算术右移1位同时阶码+1.
- 注:采用双符号位的好处是出现溢出还可以挽救。
- 规格化浮点数的特点:
- 用原码表示的尾数进行规格化:
正数为0.1....的形式,其最大值表示为0.11...1; 最小值表示为0.10...0;所以尾数的表示范围为\(1/2 \leq M \leq (1-2^{-n})\)
负数为1.1....的形式,其最大值表示为1.10...0; 最小值表示为1.11...1;所以尾数的表示范围为\(-(1-2^{-n} \leq M \leq -1/2)\) - 用补码表示的尾数进行规格化:
正数和原码相同。
负数:计算机规定,补码表示的负数尾数数值位的第一位必须是0,才算合法的规格化,其最大值表示为1.01...1; 最小值表示为1.00...0.
例:
解:将尾数算术左移3位,低位补0.
尾数变为1.0100000,左归了3次,阶码-3,变为0011.
- 浮点数的溢出:
如果出现正下溢或负下溢的情况通常当作机器数的0处理。
出现正上溢或负上溢,则抛出异常。
2.3.2 IEEE 754
IEEE 754是浮点数的一个标准
移码
- 在IEEE 754标准中,阶码用移码表示。
- 移码是在补码的基础上将符号位取反,只能用于表示整数。
- 移码的定义:移码 = 真值 + 偏置值
n位移码的偏置值一般为为\(2^{n-1}\),也可以取其他值。在IEEE 754标准中,规定阶码用移码表示,偏置值设为\(2^{n-1} - 1\)
IEEE 754标准
- IEEE 754标准的浮点数格式如下图所示:
- IEEE 754的浮点数格式规定如下表:
类型 | 数符 | 阶码 | 尾数数值 | 总位数 | 偏置值 |
---|---|---|---|---|---|
float | 1 | 8 | 23 | 32 | 7FH/127 |
double | 1 | 11 | 52 | 64 | 3FFH/1023 |
long double | 1 | 15 | 64 | 80 | 3FFFH/16383 |
- 尾数部分用源码表示,规格化希望原码最高数值位为1,所以在IEEE 754标准中,默认原码的最高数值位为1,这样float的尾数数值位虽然有23位,但是实际可以表示24位数。
表中每个数据类型的阶码、尾数、总位数需要记住,考试不会给。 - 例:float:1000 0001 1000 1010 0101 0000 1000 0000
解:最前面的1表示整个数的正负
阶码真值 = 移码 - 偏移量
最终,这个浮点数的真值为:
-
例:将十进制数-0.75转换为IEEE 754标准的单精度浮点数格式表示:
解:\((-0.75)_{10} = (-0.11)_2 = (-1.1)_2 \times 2^{-1}\)
移码 = 阶码真值(补码表示) + 偏移量(补码表示)
-1的解码表示为1111 1111(-1) + 0111 1111(127) = 0111 1110(留足8位)
-0.75 = float:10111 1110100 0000 0000 0000 0000 0000 -
例:IEEE 754标准的float = C0 A0 00 00 H的十进制是多少?
解:C0 A0 00 00 H对应二进制:1100 0000 1010 0000 0000 0000 0000 0000
数符 = 1
移码 = 100 0000 1
尾数 = 1.010 0000 0000 0000 0000
解码真值 = 移码 - 偏移量
= 1000 0001 + 1000 0001
= 0000 0010
= 2
对应十进制 = \(N = (-1) \times 2^2 \times 1.01 = -4 \times 1.25 = -5\) -
IEEE 754 float能表示的最小绝对值和最大绝对值分别是多少?
- 最小绝对值:尾数全0,阶码真值最小-126,对应移码机器数0000 0001,此时整体的真值为\((1.0)_2 \times 2^{-126}\)
- 最大绝对值:尾数全1,阶码真值最大127,对应移码机器数1111 1110,此时整体的真值为\((1.111...11)_2 \times 2^{127}\)
- 注:IEEE 754的移码全零和全一是特殊值,所以这里阶码真值最小到-126,最大到127.
- 如果阶码全为0,默认尾数乘2的-126次方,但是尾数默认省略的首位1变成0,也就是阶码全为0时,可以表示绝对值更小的数。
- 如果阶码全为0,尾数也全为0,表示真值\(\pm 0\)
- 当阶码全为1,尾数全为0,表示无穷大\(\pm \infty\)。当计算过程中出现正上溢或负上溢时,结果会被记为\(+\infty\)或\(-\infty\)。
- 当阶码全1,尾数不全为0时,表示非数值“NaN”(Not A Number)。
2.3.3 浮点数的运算
浮点数的加减运算
浮点数加减运算的步骤:
- 对阶:阶数小的向阶数大的转换(因为计算机内部,尾数是定点小数,如果阶大的转阶小的,尾数可能表示不下。)
- 尾数加减:将尾数进行相加
- 规格化:保证尾数是规格化的
- 舍入:由于浮点数的位数是有限的,超出有效部分的需要被舍弃。
- 判溢出:如果阶码的值超出规定范围,则判断溢出。
-
例:已知十进制数X = -5/256、Y = +59/1024,按机器补码浮点运算规则计算X-Y,结果用二进制表示。浮点数格式如下:阶符取2位,阶码取3位,数符取2位,尾数取9位。(这个不是IEEE 754标准)
-
解:
转换格式:
5D = 101B
1/265 = \(2^{-8}\)
\(X = -101 \times 2^{-8} = -0.0101 \times 2^{-5} = -0.101 \times 2^{-101}\)
59D = 11 1011B
1/1024 = \(2^{-10}\)
\(Y = +111011 \times 2^{-10} = +0.111011 \times 2^{-4} = +0.111011 \times 2^{-100}\)
X阶码-101转为补码:11 011(双符号位)
X尾数-0.101原码是1.101,补码为11.0110 0000 0
综上:X = 11011,11.011000000;
同理,Y = 11100,00.111011000。
对阶
使两个数的阶码相等,小阶向大阶看齐、尾数右移1为,阶码+1.
求阶差:\(\Delta E_补 = 11011+00100 = 11111 = -1\)
对阶:X:11011,11.011000000 -> 11000,11.101100000.(算术右移)
尾数加减:最终结果10.110001000(发生溢出,但是还可以挽救)
规格化:
尾数算术右移,阶码加一。得到结果:
X-Y:11101,11.011000100
舍入:无舍入
判溢出:阶码没有溢出,结果真值为\(2^{-3} \times (-0.1001111)_2\) -
浮点数加减运算——舍入:
- 0舍1入法:类似四舍五入,尾数是0直接舍弃,尾数是1末尾+1.
- 恒置1法,尾数右移时,不论丢掉的最高数值位是0还是1,都使右移后的尾数末位恒置1. 这种做法可能使尾数变大或变小。
浮点数到定点数的强制类型转换
不同字长的机器各类型数据位数如下表:
类型 | 16位机器 | 32位机器 | 64位机器 |
---|---|---|---|
char | 8 | 8 | 8 |
short | 16 | 16 | 16 |
int | 16 | 32 | 32 |
long | 32 | 32 | 64 |
long long | 64 | 64 | 64 |
float | 16 | 32 | 32 |
double | 64 | 64 | 64 |
- 无损类型转换(32位机):char -> int -> long -> double(64位的long转double会有精度的丢失)
float -> double - 32位机器,int转float:
int表示整数,1位符号位,31位数值位。
float表示整数及小数,1位符号位,8位阶码位,23+1(隐藏的首位1) = 24位数值位。
所以int -> float可能损失精度。
float -> int可能溢出及损失精度。