• 第 2 章 数据的表示和运算

    Intro

    【考纲内容】

    1. 数制与编码 进位计数制及其相互转换;定点数的编码表示
    2. 运算方法和运算电路 基本运算部件:加法器,算法逻辑单元(ALU) 加/减运算:补码加/减运算器,标志位的生成 乘/除运算:乘/除法运算的基本原理,乘法运算和除法电路的基本结构
    3. 整数的表示和运算 无符号整数的表示和运算:带符号整数的表示和运算
    4. 浮点数的表示和运算 浮点数的表示:IEEE754 标准;浮点数的加/减运算

    【复习提示】

    本章内容较为繁杂,由于计算机中数的表示和运算方法与人们日常生活中的表示和运算方法不同,因此理解也较为困难。纵观近几年的真题,不难发现 unsigned、short、int、long、float、double 等在C语言中的表示、运算、溢出判断、隐式类型转换、强制类型转换、 IEEE754 浮点数的表示,以及浮点数的运算, 都是考研考查的重点,需要牢固掌握。

     

    一、数制与编码

    0x00 进位计数制及其相互转换

    在计算机系统内部,所有的信息都是用二进制进行编码的,这样做的原因有以下几点。

    1. 二进制只有两种状态,使用有两个稳定状态的物理器件就可以表示二进制数的每一位,制造成本比较低,例如用高低电平或电荷的正负极性都可以很方便地表示 0 和 1
    2. 二进制位 1 和 0 正好与逻辑值“真”和“假”对应,为计算机实现逻辑运算和程序中的逻辑判断提供了便利条件。
    3. 二进制的编码和运算规则都很简单,通过逻辑门电路能方便地实现算术运算。

    1. 进位计数法

    常用的进位计数法有十进制、二进制、八进制、十六进制等。十进制数是日常生活中最常使用的,而计算机中通常使用二进制数、八进制数和十六进制数。 在进位计数法中,每个数位所用到的不同数码的个数称为基数。十进制的基数为10(0~9),每个数位计满10就向高位进位,即“逢十进一” 十进制数101,其个位的1显然与百位的1所表示的数值是不同的。每个数码所表示的数值等于该数码本身乘以一个与它所在数位有关的常数,这个常数称为位权。一个进位数的数值大小就是它的各位数码按权相加。

    一个 r 进制数 (KnKn1K0K1Km) 的数值可表示为:

    Knrn+Kn1rn1++K0r0+K1r1++Kmrm=i=nmKiri

    式中,r 是基数;ri 是第 i 位的位权(整数位最低位规定为第0位);Ki 的取值可以是 0,1,,r1r 个数码中的任意一个。

    1. 二进制。计算机中用得最多的是基数为 2 的计数制,即二进制。二进制只有 0 和 1 两种数字符号,计数“逢二进一”。它的任意数位的权为 2ii 为所在位数。
    2. 八进制。八进制作为二进制的一种书写形式,其基数为 8,有 0~7 共 8 个不同的数字符号,计数“逢八进一”。因为 r=8=23 ,所以只要把二进制中的 3 位数码编为一组就是一位八进制数码,两者之间的转换极为方便。
    3. 十六进制。十六进制也是二进制的一种常用书写形式,其基数为 16,“逢十六进一”。每个数位可取 0~9、A、B、C、D、E、F中的任意一个,其中A、B、C、D、E、F分别表示 10~15。因为 r=16=24,因此 4 位二进制数码与1位十六进制数码相对应。

     

    2. 不同进制数之间的相互转换(略)

    3. 真值和机器数

    在日常生活中,通常用正号、负号来分别表示正数(正号可省略)和负数,如 +15、-8 等。这种带“+”或“-”符号的数称为真值。真值是机器数所代表的实际值。

    在计算机中,通常将数的符号和数值部分一起编码,将数据的符号数字化,通常用“0”表示“正”,用“1”表示“负”。这种把符号“数字化”的数称为机器数。常用的有原码补码反码表示法。如 0,101(这里的逗号 “,” 仅为区分符号位与数值位)表示 +5。

     

    0x01 BCD 码

    二进制编码的十进制数(Binary-Coded Decimal,BCD)通常采用 4位二进制数来表示一位十进制数中的 0~9 这10个数码。这种编码方法使二进制数和十进制数之间的转换得以快速进行。但 4 位二进制数可以组合出 16 种代码,因此必有 6 种状态为冗余状态。

    下面列举几种常用的BCD码。

    1. 8421 码(常用)。它是一种有权码,设其各位的数值为 b3,b2,b1,b0,则权值从高到低依次为 8,4,2,1,它表示的十进制数为 D=8b3+4b2+2b1+1b0。 如 81000;91001 若两个 8421 码相加之和小于或等于 (1001)2(9)10,则不需要修正;若相加之和大于或等于 (1010)2(10)10,则要加 6 修正(从 10101111 这6个为无效码,当运算结果落于这个区间时,需要将运算结果加上 6),并向高位进位。 2
    2. 余 3 码。这是一种无权码,是在 8421码的基础上加 (0011)2 形成的,因每个数都多余 “3” 因此称为余 3 码。如 81011;91100
    3. 2421码。这也是一种有权码,权值由高到低分别为 2,4,2,1,特点是大于或等于 5 的 4 位二进制数中最高位为 1,小于 5 的最高位为 0。如 51011 而非 0101。

     

    0x02 定点数的编码表示

    根据小数点的位置是否固定,计算机中有两种数据格式:定点表示浮点表示

     

    1. 机器数的定点表示

    定点表示法用于表示定点小数和定点整数。

    1. 定点小数:纯小数,约定小数点位置在符号位之后、有效数值部分最高位之前。若数据 X 的形式为 X=x0x1xn,(其中 x0 为符号位,x1xn 是数值的有效部分,也称尾数,x1 为最高有效位),则在计算机中的表示形式如下图所示。

    2-1

    1. 定点整数:纯整数,约定小数点位置在有效数值部分最低位之后。若数据 X 的形式为 X=x0x1x2xn,(其中 x0 为符号位,x1xn 是尾数,xn 为最低有效位),则在计算机中的表示形式如下图所示。

    2-2

    定点数编码表示法主要有以下四种:原码、补码、反码和移码。

     

    2. 原码、补码、反码、移码

    (1) 原码表示法

    用机器数的最高位表示数的符号,其余各位表示数的绝对值。

    [x]={x,0x<11|x|,1<x<0

    例如,若 x1=+0.1101,x2=0.1101,字长为 8 位,则其原码表示为:

    [x1]=0.1101000
    [x2]=1(0.1101)=1.1101000

    其中最高位是符号位。

    若字长为 n+1,则原码小数的表示范围为 (12n)x12n(关于原点对称)。

    [x]={0,x,0x<2n2n|x|,2n<x<0

    例如,若 x1=+1110,x2=1110,字长为 8 位,则其原码表示为:

    [x1]=0,0001110
    [x2]=27+1110=1,0001110

    其中最高位是符号位。

    若字长为 n+1,则原码整数的表示范围为 (2n1)x2n1(关于原点对称)。

    注意:真值零的原码表示有正零和负零两种形式,即 [+0]=00000[0]=10000

    优点:原码表示与真值的对应关系简单、直观,与真值的转换也简单,并且用原码实现乘除运算比较方便。

    缺点:零的表示不唯一,更重要的是原码加减运算比较复杂。

     

    (2) 补码表示法

    原码加减运算规则比较复杂,对于两个不同符号数的加法(或同符号数的减法),先要比较两个数的绝对值大小,然后用绝对值大的数减去绝对值小的数,最后还要给结果选择合适的符号。而补码表示法中的加减运算则统一采用加法操作实现。

    纯小数的补码定义(了解)
    [x]={x,0x<12+x=2|x|,1x<0(mod2)

    例如,若 x1=+0.1001,x2=0.0110 ,字长为 8 位,则其补码表示为 [x1]=0.1001000,[x2]=280.0110=1.1010000

    若字长为 n+1,则补码的表示范围为 1x12n(比原码多表示 -1)。

     

    纯整数的补码定义
    [x]={0,x,xx<2n2n+1+x=2n+1|x|,2nx<0(mod2n+1)

    例如,若 x1=+1010,x2=1101, 字长为 8 位,则其补码表示为 [x1]=0,0001010,[x2]=280,0001101=1,1110011

    若字长为 n+1,则补码的表示范围为 2nx2n1(比原码多表示 2n)。

    注意:零的补码表示是唯一的,即 [+0] = [-0]=0.0000。 由定义 [-1]= 10.0000- 1.0000 =1.0000,可见,小数补码比原码多表示一个 -1;整数补码比原码多表示一个 2n

     

    变形补码

    变形补码,又称模 4 补码,双符号位的补码小数,其定义为

    [x]={x,0x<14+x=4|x|,1x<0(mod4)

    模 4 补码双符号位 00 表示正,11 表示负,用在完成算术运算的 ALU 部件中。 将 [x] 的符号位与数值位一起右移并保持原符号位的值不变,可实现除法功能。

     

    补码与真值之间的转换

    对补码而言,正数和负数的转换不同。正数补码的转换方式与原码的相同。

    (3) 反码表示法(了解)

    反码表示法的负数是通过“各位取反,末位减 1”来得到的。正数的反码表示与其补码或原码表示相同。反码表示的主要缺点如下:

    由于上述缺点,反码在计算机中很少使用,通常作为数码变换的中间表示形式。

     

    (4) 移码表示法

    移码常用来表示浮点数的阶码,只能表示整数。移码是在真值 X 上加上一个常数(偏置值),通常取 2n,相当于 X 在数轴上向正方向偏移了若干单位,因此称为“移码”。

    移码定义为:

    [x]=2n+x(2nx<2n)

    其中机器字长为 n+1

    例如,若正数 x=+10101,负数 x2=10101,字长为 8 位,则其移码表示为:

    [x]=27+10101=1,0010101
    [x2]=27+(10101)=0,1101011

    移码具有以下特点:

    1. 移码全为 0 时,对应真值的最小值 2n
    2. 移码全为 1 时,对应真值的最大值 2n1
    3. 移码保持了数据原有的大小顺序:移码大,真值就大;移码小,真值就小。

     

    编码表示总结

    1. 原码、补码、反码的符号位相同,正数的机器码相同。
    2. 原码和反码在数轴上对称,都存在 +0 和 -0 两个零。
    3. 补码和移码在数轴上不对称,零的表示唯一,它们比原码和反码多表示一个数。
    4. 整数的补码和移码的符号位相反,数值位相同。
    5. 负数的反码和补码末位相差 1。
    6. 原码容易判断大小,而负数的反码和补码难以直接判断大小,可用以下规则快速判断:对于负数,数值部分越大,绝对值越小,真值越大(更接近 0)。

     

    0x03 整数的表示

    1. 无符号整数的表示

    当一个编码的全部二进制位均为数值位而没有符号位时,该编码表示就是无符号整数,也直接称为无符号数。此时,默认数的符号为正。由于无符号整数省略了一位符号位,所以在字长相同的情况下,它能表示的最大数比带符号整数能表示的大。例如,8 位无符号整数,对应的表示范围为 0~28-1,即最大数为 255,而 8 位带符号整数的最大数是 127。

    一般在全部是正数运算且不出现负值结果的场合下,使用无符号整数表示。例如,可用无符号整数进行地址运算,或用它来表示指针。

    2. 带符号整数的表示

    将符号数值化,并将符号位放在有效数字的前面,就组成了带符号整数。虽然前面介绍的原码、补码、反码和移码都可以用来表示带符号整数,但补码表示有其明显的优势:

    1. 与原码和反码相比,0 的补码表示唯一
    2. 与原码和移码相比,补码运算规则比较简单,且符号位可以和数值位一起参加运算
    3. 与原码和反码相比,补码比原码和反码多表示一个最小负数。计算机中的带符号整数都用补码表示,故 n 位带符号整数的表示范围是 2n12n11

     

    二、运算方法和运算电路

    0x00 基本运算部件

    在计算机中,运算器由算术逻辑单元(Arithmetic Logic Unit,ALU)、移位器、状态寄存器和通用寄存器组等组成。运算器的基本功能包括加、减、乘、除四则运算,与、或、非、异或等逻辑运算,以及移位、求补等操作。ALU 的核心部件是加法器。

    1. 一位全加器

    全加器(FA)是最基本的加法单元,具有加数 Ai、加数 Bi 与低位传来的进位 Ci1 共三个输入,本位和 Si 与向高位的进位 Ci 共两个输出。全加器的逻辑表达式如下:

    一位全加器对应的逻辑结构如下图所示:

    3

    2. 串行进位加法器

    将 n 个全加器相连可得到 n 位加法器,称为串行进位加法器,如下图所示。串行进位又称行波进位,每级进位直接依赖于前一级的进位,即进位信号是逐级形成的。

    4

    图 2.4 中的加法器实现了两个 n 位二进制数 A=AnAn1A1B=BnBn1B1 逐位相加的功能,得到的二进制和为 S=SnSn1S1,进位输出为 Cn。例如,当 A=1111B=0001 时,结果输出为 S=0000Cn=1。由于位数有限,高位自动丢失,所以实际是模 2n 的加法运算。

    在串行进位加法器中,低位运算产生进位所需的时间将影响高位运算的时间。因此,串行进位加法器的最长运算时间主要由进位信号的传递时间决定,位数越多延迟时间就越长,而全加器本身的求和延迟只为次要因素,因此加快进位产生和提高传递的速度是关键。

    3. 并行进位加法器

    Gi=AiBiPi=AiBi,全加器的进位表达式为:

    Ci=Gi+PiCi1

    其中,Gi进位产生函数(本地进位),表示当 AiBi 都为 1 时,Ci=1 Pi 为进位传递函数,表示当 AiBi=1Ci1=1 时,Ci=1

    代入前面 C1,C2,,Cn 的公式,可得:

    C1=G1+P1C0
    C2=G2+P2C1=G2+P2G1+P2P1C0
    C3=G3+P3C2=G3+P3G2+P3P2G1+P3P2P1C0
    Cn=Gn+PnCn1=Gn+PnGn1+PnPn1Gn2++PnPn1P1C0

    从上述表达式可以看出,Ci 仅与 Ai,Bi 及最低进位 C0 有关,相互间的进位没有依赖关系。只要 A1An,B1BnC0 同时到达,就可几乎同时形成 C1Cn,并且同时生成各位的和。实现上述逻辑表达式的电路称为先行进位(或超前进位)部件,简称 CLA 部件,如下图所示。

    通过这种进位方式实现的加法器称为全先行进位加法器。因为各个进位是并行产生的,所以是一种并行加法器。

    5

    这种进位方式是快速的,与位数无关。但随着加法器位数的增加,Ci 的逻辑表达式会变得越来越长,这会使电路结构变得很复杂。因此,当位数较多时,采用全先行进位是不现实的。 更多位数的加法器可通过将 CLA 部件或全先行进位加法器串接起来实现。例如,对于 16 位加法器,可以分成 4 组,组内为 4 位先行进位,组间串行进位。为了进一步提高运算速度,也可以采用组内和组间都并行的进位方式。

    4. 带标志加法器

    无符号数加法器只能用于两个无符号数相加,不能进行带符号整数的加/减运算。为了进行带符号整数的加/减运算,还需要在无符号数加法器的基础上增加相应的逻辑门电路,使加法器不仅能计算和/差,还要生成相应的标志信息。如下图所示:

    6

    在图 2.6 中,溢出标志的逻辑表达式为 OF=Cn1Cn;符号标志是和的符号,即 SF=Sn1;零标志 ZF=1 当且仅当 S=0;进位/借位标志 CF=Cn,即当 C0=0 时,CF 为进位 Cn,当 C0=1 时,CF 为进位 Cn 取反。

    5. 算术逻辑单元(ALU)

    ALU 是一种功能较强的组合逻辑电路,它能进行多种算术运算和逻辑运算。由于加、减、乘、除运算最终都能归结为加法运算,因此 ALU 的核心是带标志加法器,同时也能执行“与”、“或”、“非”等逻辑运算。ALU 的基本结构如下图所示:

    7

    其中

    ALUop 的位数决定了操作的种类,如位数是 3 时,ALU 最多只有 23=8 种操作。下图展示了一个可以完成”与、或、加“的一位 ALU 结构图。

    8

    注意:如对电路基础知识不太熟悉,可参阅电路相关教材的基础部分。对此节电路内容亦不必过分深究,目前统考对电路的要求并不高,很少涉及。

     

    0x01 定点数的移位运算

    1. 算术移位

    算术移位的对象是有符号数,在移位过程中符号位保持不变。对于正数,由于 [x]=[x]=[x],因此移位后出现的空位均以 0 填补。对于负数,由于原码、补码、反码的表示形式不同,因此当机器数移位时,对其空位的填补规则也不同。

    表 2.1 不同机器数算术移位后的空位填补规则:

    t1

    对于带符号数,左移一位若不产生溢出,相当于乘以 2 右移一位,相当于除以 2

    从表 2.1 可以得出以下结论:

    1. 负数的原码:数值部分与真值相同,故移位时只要符号位不变,空位均填 0。
    2. 负数的反码:各位除符号位外与负数的原码相反,故移位后空位填 1。
    3. 负数的补码:找到第一个“1”时,在此“1”左边的各位均与对应的反码相同,而在此“1”右边的各位(包括此“1”在内)均与对应的原码相同。 故负数的补码左移时,因空位出现在低位,填 0;右移时因空位出现在高位,填 1。

    总结:

    2. 逻辑移位

    逻辑移位将操作数视为无符号数。移位规则如下:

    3. 循环移位

    循环移位的特点是移出的数位又被移入数据中,而是否带进位则取决于是否将进位标志位加入循环位移。例如,带进位位的循环左移如图 2.9(d) 所示,即数据位连同进位标志位一起左移,数据的最高位移入进位标志位 CF,而进位位则依次移入数据的最低位。循环移位操作特别适合将数据的低字节数据和高字节数据互换。

    9

     

    0x02 定点数的加减运算

    在机器内部并没有小数点,只是人为约定了小数点的位置,小数点约定在最左边就是定点小数,小数点约定在最右边就是定点整数。因此,在运算过程中,无需考虑对应的定点数是小数还是整数,只需关心它们的符号位和数值位

     

    1. 补码的加减法运算

    补码加减运算规则简单,易于实现。补码加减运算的公式如下(设机器字长为 n+1):

    [A+B]=[A]+[B]mod2n+1
    [AB]=[A]+[B]mod2n+1

    补码运算的特点如下:

    1. 按二进制运算规则进行,逢二进一。
    2. 若做加法,两数的补码直接相加;若做减法,则将被减数与减数的机器负数相加。
    3. 符号位与数值位一起参与运算,加、减运算结果的符号位也在运算中直接得出。
    4. 最终运算结果的高位丢弃,保留 n+1 位,运算结果亦为补码。

    例题: 设机器字长为 8 位(含 1 位符号位),A=15B=24,求 [A+B][AB]

    解:A=+15=+0001111,B=+24=+0011000; 得 [A]=00001111[B]=00011000。求得 [B]=11101000

    所以

    [A+B]=00001111+00011000=00100111

    符号位为 0,对应真值为 +39

    [AB]=[A]+[B]=00001111+11101000=11110111

    符号位为 1,对应真值为 9

     

    2. 补码加减运算电路

    已知一个数的补码表示为 Y,则这个数的负数的补码为 Y+1。因此,只要在原加法器的 Y 输入端加 n 个反向器以实现各位取反的功能,然后加一个 2 选 1 多路选择器,用一个控制端 Sub 来控制,以选择是将 Y 输入加法器还是将 Y 输入加法器,并将控制端 Sub 同时作为低位进位送到加法器,如下图所示。该电路可实现补码加减运算。

    10

    图 2.10 中的加法器是带标志加法器。无符号整数的二进制表示相当于正整数的补码表示,因此,该电路同时也能实现无符号整数的加/减运算。对于带符号整数 xy,图中 XY 分别是 xy 的补码表示;对于无符号整数 xy,图中 XY 分别是 xy 的二进制表示。

    标志位的含义如下:

     

    3. 溢出判别方法

    仅当两个符号相同的数相加或两个符号相异的数相减才可能产生溢出,如两个正数相加,结果的符号位为 1(结果为负);一个负数减去一个正数,结果的符号位为 0(结果为正)。补码定点数加减运算溢出判断的方法有三种:

    1. 采用一位符号位:由于减法运算在机器中是用加法器实现的,因此无论是加法还是减法,只要参加操作的两个数符号相同,结果又与原操作数符号不同,则表示结果溢出。设 A 的符号为 AsB 的符号为 Bs,运算结果的符号为 S,则溢出逻辑表达式为:

      V=AsBsS+AsBsS

      V=0,表示无溢出;若 V=1,表示有溢出。

    2. 采用双符号位:双符号位法也称模 4 补码。运算结果的两个符号位 Ss1Ss2 相同,表示未溢出;运算结果的两个符号位 Ss1Ss2 不同,表示溢出,此时最高位符号位代表真正的符号。符号位 Ss1Ss2 的各种情况如下:

      1. Ss1Ss2=00:表示结果为正数,无溢出。
      2. Ss1Ss2=01:表示结果正溢出。
      3. Ss1Ss2=10:表示结果负溢出。
      4. Ss1Ss2=11:表示结果为负数,无溢出。

      溢出逻辑判断表达式为:

      V=Ss1Ss2

      V=0,表示无溢出;若 V=1,表示有溢出。

    3. 根据数据位的进位情况判断溢出:若符号位的进位 Cn 与最高数位的进位 Cn1 相同,则说明没有溢出,否则表示发生溢出。溢出逻辑判断表达式为:

      V=CnCn1

      V=0,表示无溢出;若 V=1,表示有溢出。

    4. 原码的加减法运算(了解)

    加法规则:先判断符号位,若相同,则绝对值相加,结果符号位不变;若不同,则做减法,绝对值大的数减去绝对值小的数,结果符号位与绝对值大的数相同。

    减法规则:两个原码表示的数相减,首先将减数符号取反,然后将被减数与符号取反后的减数按原码加法进行运算。注意运算时机器字长,当左边位出现溢出时,将溢出位丢掉。

     

    0x03 定点数的乘除运算

    1. 定点数的乘法运算

    乘法运算由累加和右移操作实现,可分为原码一位乘法和补码一位乘法。

    (1) 原码一位乘法

    乘积的数值部分是两个数的绝对值相乘之积。设 [X]=xsxn1x0[Y]=ysyn1y0,则运算规则如下:

    1. 被乘数和乘数均取绝对值参加运算,看作无符号数,符号位为 xsys
    2. 部分积是乘法过程的中间结果。乘数的每一位 yi 乘以被乘数得 X×yi,然后将该结果与前面所得的结果累加,即为部分积,初值为 0。
    3. 从乘数的最低位 y0 开始判断:若 yi=1,则部分积加上被乘数 X,然后右移一位;若 yi=0,则部分积加上 0,然后右移一位。
    4. 重复上述步骤,判断 n 次。

    由于参与运算的是两个数的绝对值,因此运算过程中的右移操作均为逻辑右移。 考虑到运算过程中部分积和乘数做加法时,可能出现部分积大于 1 的情况(产生进位),但此刻并非溢出,所以部分积和被乘数取双符号位

    例题:设 x=0.1101,y=0.1011,采用原码一位乘法求 xy |x|=00.1101,|y|=00.1011 过程如下:

    x

    符号位 Ps=xsys=1,xy=0.10001111

    (2) 无符号数乘法运算电路

    图 2.11 是实现两个 32 位无符号数乘法的逻辑结构图。 在图 2.11 中,部分积和被乘数 X 做无符号数加法时,可能产生进位,因此需要一个专门的进位位 C。乘积寄存器 P 初始时置 0。计数器 Cn 初值为 32,每循环一次减 1。ALU 是乘法器核心部件,对乘积寄存器 P 和被乘数寄存器 X 的内容做“无符号加法”运算,运算结果送回寄存器 P,进位存放在 C 中。每次循环都对进位位 C、乘积寄存器 P 和乘数寄存器 Y 实现同步“逻辑右移",此时,进位位 C 移入寄存器 P 的最高位,寄存器 Y 的最低位移出。每次从寄存器 Y 移出的最低位都被送到控制逻辑,以决定被乘数是否“加”到部分积上。

    image

    (3) 补码一位乘法(Booth 算法)

    Booth 算法是一种有符号数的乘法,采用相加和相减操作计算补码数据的乘积。 [X]=xs,x1x2xn,[Y]=ys,y1y2yn,运算规则如下:

    1. 符号位参与运算,运算的数均以补码表示。
    2. 被乘数一般取双符号位参与运算,部分积取双符号位,初值为 0,乘数取单符号位。
    3. 乘数末位增设附加位 yn+1,初值为 0。
    4. 根据 (yi,yi+1) 的取值来确定操作,见表 2.2。
    5. 移位按补码右移规则进行。
    6. 按照上述算法进行 n+1 步操作,但第 n+1 步部分积右移一位不再移位(共进行 n+1 次累加和 n 次右移),仅根据 yiyi+1 的比较结果做相应的运算。

    表 2.2 如下所示:

    yn(高位)yn+1 (低位)操作
    00部分积右移一位
    01部分积加 [X],右移一位
    10部分积加 [X],右移一位
    11部分积右移一位

    例题:设 x=0.1101,y=0.1011,采用 Booth 算法求 xy [x]=11.0011,[x]=00.1101,[y]=0.1011,求解过程如下:

    8

    所以 [xy]=1.01110001,xy=0.10001111

     

    (4) 补码乘法运算电路

    图 2.12 是实现 32 位补码一位乘法的逻辑结构图,和图 2.11 所示的逻辑结构很相似。因为是带符号数运算,不需要专门的进位位。每次循环,乘积寄存器 P 和乘数寄存器 Y 实现同步“算术右移”,每次从寄存器 Y 移出的最低位和它的前一位来决定是 [X][+X] 还是 +0

    12

     

    2. 定点数的除法运算

    除法运算可转换成”累加-左移“(逻辑左移),分为原码除法和补码除法。

    (1) 符号扩展

    在算术运算中,有时必须把带符号的定点数转换成具有不同位数的表示形式。例如,某个程序需要将一个 8 位整数与另外一个 32 位整数相加,要想得到正确的结果,在将 8 位整数与 32 位整数相加之前,必须将 8 位整数转换成 32 位整数形式,这称为“符号扩展”。 正数的符号扩展非常简单,即符号位不变,新表示形式的所有扩展位都用 0 填充。 负数的符号扩展方法则根据机器数的不同而不同。 原码表示负数的符号扩展方法与正数相同,只不过此时符号位为 1。 补码表示负数的符号扩展方法是:原有形式的符号位移动到新形式的符号位上,新表示形式的所有附加位都用 1(对于整数)或 0(对于小数)填充。

    (2) 原码除法运算(不恢复余数法)

    原码不恢复余数法也称原码加减交替除法。特点是商符和商值是分开进行的,减法操作用补码加法实现,商符由两个操作数的符号位“异或”形成。求商值的规则如下:

    设被除数 [X]=xsx1x2xn,除数 [Y]=ysy1y2yn,则:

    1. 商的符号:Ωs=xsys
    2. 商的数值:|Q|=|X|/|Y|

    |Q| 的不恢复余数法运算规则如下:

    1. 先用被除数减去除数 (|X||Y|=|X|+(|Y|)=|X|+[|Y|]),当余数为正时,商上 1,余数和商左移一位,再减去除数;当余数为负时,商上 0,余数和商左移一位,再加上除数
    2. 当第 n+1 步余数为负时,需加上 |Y| 得到第 n+1 步正确的余数(余数与被除数同号)

    例题:设 x=0.1011,y=0.1101,采用原码加减交替除法求 x/y |x|=0.1011,|y|=0.1101,[|y|]=0.1101,[|y|]=1.0011

    9

    因此 Qs=xsys=0,则 x/y=+0.11010.0111×24

    (3) 补码除法运算(加减交替法)

    补码一位除法的特点是,符号位与数值位一起参加运算,商符自然形成。除法第一步根据被除数和除数的符号决定是做加法还是减法;上商的原则根据余数和除数的符号位共同决定,同号上商“1”,异号上商“0”;最后一步商恒置“1”。加减交替法的规则如下:

    1. 符号位参加运算,除数与被除数均用补码表示,商和余数也用补码表示。
    2. 若被除数与除数同号,则被除数减去除数;若被除数与除数异号,则被除数加上除数。
    3. 若余数与除数同号,则商上 1,余数左移一位减去除数;若余数与除数异号,则商上 0,余数左移一位加上除数。
    4. 重复上述操作 n 次。
    5. 若对商的精度没有特殊要求,则一般采用“末位恒置 1”法。

    例题:设 x=0.1000,y=0.1011,采用补码交替法求 x/y 采用两位符号表示:[x]=00.1000,[x]=00.1000,[y]=11.1011,[y]=11.0101,[y]=00.1011

    10

    因此 [x/y]=1.0101,余 0.0111×24

    (4) 除法运算电路

    图 2.13 是一个 32 位除法逻辑结构图,它和乘法逻辑结构也很相似。

    初始时,寄存器 R 存放扩展被除数的高位部分,寄存器 Q 存放扩展被除数的低位部分。ALU 是除法器核心部件,对余数寄存器 R 和除数寄存器 Y 的内容做加/减运算,运算结果送回寄存器 R。每次循环,寄存器 RQ 实现同步左移,左移时,Q 的最高位移入 R 的最低位,Q 中空出的最低位被上商。每次由控制逻辑根据 ALU 运算结果的符号来决定上商是 0 还是 1。

    13

     

    0x04 C 语言中的整数类型及类型转换

    1. 有符号数和无符号数的转换

    C 语言允许在不同的数据类型之间进行强制类型转换。对于两者都能表示的数值,转换过程中数值本身不发生变化。但是,对于无法表示的数值,转换结果可能会产生不一致性。

    观察如下代码:

    在采用补码表示法的机器上,上述代码会输出:

    虽然 y 的值似乎与 x 没有关系,但将这两个数转换为二进制表示时,可以发现其中的规律。表 2.3 显示了 x 为补码表示,y 为无符号的二进制真值。

    表 2.3 yx 的对比

    变量二进制表示
    x-43211110111100011111 (补码)
    y612151110111100011111 (无符号二进制真值)

    强制类型转换的结果保持位值不变,仅改变了解释这些位的方式。

    再来看一个 unsigned short 型转换到 short 型的例子:

    在采用补码的机器上,上述代码会输出:

    通过将这两个数用二进制表示,可以证实我们之前得出的结论。

    2. 不同字长整数之间的转换

    另一种常见的转换是在不同字长的整数之间进行数值转换。

    观察如下代码:

    这段程序的输出如下:

    xyuv 的十六进制表示分别为 0x000286A10x86A10xFFFF77510x7751。大字长变量向小字长变量转换时,系统直接截断高位部分,低位直接赋值。因此也是一种保持位值的处理方法。

    再来看小字长变量向大字长变量转换的情况:

    运行结果如下:

    xyuv 的十六进制表示分别是 0xEFAF0xFFFFEFAF0xEFAF0x0000EFAF。短字长到长字长的转换不仅要使相应的位值相等,还要对高位部分进行扩展:

    注意,char 类型为 8 位无符号整数,在转换为 int 时高位补 0 即可。

     

    0x05 数据的存储和排列

    1. 数据的“大端方式”和“小端方式”存储

    在存储数据时,数据从低位到高位可以按从左到右排列,也可以按从右到左排列。因此,无法用最左或最右来表征数据的最高位或最低位,通常用最低有效字节(LSB)和最高有效字节(MSB)来分别表示数的低位和高位。例如,在 32 位计算机中,一个 int 型变量 i 的机器数为 01 23 45 67H,其最高有效字节 MSB=01H,最低有效字节 LSB=67H。

    现代计算机基本上都采用字节编址,即每个地址编号中存放 1 字节。不同类型的数据占用的字节数不同。假设变量 i 的地址为 0800H,字节 01H23H45H67H 应该各有一个内存地址,那么地址 0800H 对应 4 字节中哪字节的地址呢?这就是字节排列顺序问题。

    多字节数据都存放在连续的字节序列中,根据数据中各字节在连续字节序列中的排列顺序不同,可以采用两种排列方式:大端方式(big endian)和小端方式(little endian),如图 2.14 所示。

    14

    大端方式按从最高有效字节到最低有效字节的顺序存储数据,即最高有效字节存放在前面;小端方式按从最低有效字节到最高有效字节的顺序存储数据,即最低有效字节存放在前面。

    在检查底层机器级代码时,需要分清各类型数据字节序列的顺序。例如,以下是由反汇编器(汇编的逆过程,即将机器代码转换为汇编代码)生成的一行机器级代码的文本表示:

    其中,“4004d3”是十六进制数表示的地址,“010564940408”是指令的机器代码,“add %eax, 0x8049464”是指令的汇编形式。该指令的第二个操作数是立即数 0x8049464。执行指令时,从指令代码的后 4 字节中取出该立即数,立即数存放的字节序列为 64H94H04H08H,正好与操作数的字节顺序相反,即采用的是小端方式存储,得到 08049464H,去掉开头的 0,得到 0x8049464。在阅读小端方式存储的机器代码时,要注意字节是按相反顺序显示的。

    2. 数据按“边界对齐”方式存储

    假设存储字长为 32 位,可按字节、半字和字寻址。对于机器字长为 32 位的计算机,数据以边界对齐方式存放,半字地址一定是 2 的整数倍,字地址一定是 4 的整数倍,这样无论所取的数据是字节、半字还是字,均可一次访存取出。

    所存储的数据不满足上述要求时,通过填充空白字节使其符合要求。这样虽然浪费了一些存储空间,但可提高取指令和取数的速度。若不对齐,则可能需要两次访存,并且对高低字节的位置进行调整、连接之后才能得到所要的指令或数据,从而影响了指令的执行效率。

    例如,“字节 1、字节 2、字节 3、半字 1、半字 2、半字 3、字 1”的数据按序存放在存储器中,按边界对齐方式和不对齐方式存放时,格式分别如图 2.15 和图 2.16 所示。

    15

    边界对齐方式相对边界不对齐方式是一种空间换时间的思想。精简指令系统计算机(RISC)通常采用边界对齐方式,因为对齐方式取指令时间相同,因此能适应指令流水。

     

    三、浮点数的表示与运算

    0x00 浮点数的表示

    1. 浮点数的表示格式

    通常,浮点数表示为 N=(1)S×M×RE,其中:

    可见浮点数由数符、尾数和阶码三部分组成。图 2.17 是一个 32 位短浮点数格式的示例。

    17

    其中,第 0 位为数符 S 第 1~7 位为移码表示的阶码 E(偏置值为 64) 第 8~31 位为 24 位二进制原码小数表示的尾数 M 基数 R 为 2。阶码的值反映浮点数的小数点的实际位置 阶码的位数反映浮点数的表示范围;尾数的位数反映浮点数的精度。

    2. 浮点数的表示范围

    原码是关于原点对称的,故浮点数的范围也是关于原点对称的,如图 2.18 所示。

    18

    当运算结果大于最大正数时称为正上溢,小于绝对值最大负数时称为负上溢,正上溢和负上溢统称为上溢。数据一旦产生上溢,计算机必须中断运算操作,进行溢出处理。当运算结果在 0 至最小数据值之间时,浮点数值趋于零,计算机仅将其当作机器零处理。

    3. 浮点数的规格化

    尾数的位数决定浮点数的有效数位,有效数位越多,数据的精度越高。为了在浮点数运算过程中保证非零浮点数在尾数的最高数位上是一个有效值,需要进行规格化操作。

    规格化浮点数的尾数 M 的绝对值应满足 1/R|M|<1。若 R=2,则有 1/2|M|<1

    4. IEEE 754 标准

    按照 IEEE 754 标准,常用的浮点数格式如图 2.19 所示。

    19

    IEEE 754 标准规定常用的浮点数格式有单精度(float 型)、双精度(double 型)和临时浮点数,基数隐含为 2,见表 2.4。

    表 2.4 IEEE 754 浮点数的格式

    类型数符阶码尾数总位数偏置值
    单精度18 位23 位32 位127
    双精度111 位52 位64 位1023
    临时浮点数115 位63 位80 位16383

    在浮点格式中,23 位尾数是纯小数。对于规格化的二进制浮点数,数值的最高位总是“1”,为了能使尾数多表示一位有效位,将这个“1”隐藏,称为隐藏位,因此 23 位尾数实际上表示了 24 位有效数字。

    浮点数真值计算公式

    其中,单精度浮点数 E 的取值范围为 1~254(8 位表示),M 为 23 位,共 32 位;双精度浮点数 E 的取值范围为 1~2046(11 位表示),M 为 52 位,共 64 位。

    表 2.5 IEEE 754 浮点数的范围

    格式最小值最大值
    单精度1.0×21262127×(2223)
    双精度1.0×2102221023×(2252)

    x

     

    5. 定点数与浮点数表示的区别

    1. 数值的表示范围:若定点数和浮点数的字长相同,则浮点表示法所能表示的数值范围远大于定点表示法。
    2. 精度:对于字长相同的定点数和浮点数来说,浮点数虽然扩大了数的表示范围,但精度降低了。
    3. 数的运算复杂度:浮点运算比定点运算复杂,因为浮点数的运算结果需要规格化。
    4. 溢出问题:在定点运算中,当运算结果超出数的表示范围时,发生溢出;浮点运算中,运算结果超出尾数表示范围不一定溢出,只有规格化后阶码超出所能表示的范围时,才发生溢出。

     

    0x01 浮点数的加减运算

    浮点数运算的特点是阶码运算和尾数运算分开进行,浮点数加减运算分为以下几步。

    1. 对阶

    对阶的目的是使两个操作数的小数点位置对齐,即使得两个数的阶码相等。步骤如下:

    1. 先求阶差。
    2. 以小阶向大阶看齐的原则,将阶码小的尾数右移一位(基数为 2),阶加 1,直到两个数的阶码相等为止。

    尾数右移时,舍弃掉有效位会产生误差,影响精度。

    2. 尾数求和

    将对阶后的尾数按定点数加(减)运算规则进行运算。运算后的尾数不一定是规格化的,因此,浮点数的加减运算需要进一步进行规格化处理。

    3. 规格化

    IEEE 754 规格化尾数的形式为 ±1.xx。尾数相加减后会得到各种可能结果,需要进行规格化操作:

    1. 右规:当结果为 ±1.xx 时,需要进行右规。尾数右移一位,阶码加 1。尾数右移时,最高位 1 被移到小数点前一位作为隐藏位,最后一位移出时,要考虑舍入。
    2. 左规:当结果为 ±0.001xx 时,需要进行左规。尾数每左移一位,阶码减 1。可能需要左规多次,直到将第一位 1 移到小数点左边。

    注意:

    4. 舍入

    运算结果需要进行舍入处理,并还原表示成 IEEE 754 格式。常见的舍入方法有:

    1. 0 舍 1 入法:类似于十进制的“四舍五入”法。运算结果保留位的最高数位为 0,则舍去;最高数位为 1,则在数的末位加 1。这样可能会使尾数溢出,此时需再做一次右规。
    2. 恒置 1 法:不论掉的最高数位是 0 还是 1,都把右移后的尾数末位恒置 1。
    3. 截断法(恒舍法):直接保留所需位数,丢弃后面的所有位,这种舍入处理最简单。

    5. 溢出判断

    在尾数规格化和尾数舍入时,可能会对阶码执行加/减运算,因此必须考虑指数溢出的问题。

    1. 指数上溢:若一个正指数超过了最大允许值(127 或 1023),则发生指数上溢,产生异常。
    2. 指数下溢:若一个负指数超过了最小允许值(-126 或 -1022),则发生指数下溢,通常把结果按机器零处理。

    右规和尾数舍入:数值很大的尾数舍入时,可能因为末位加 1 而发生尾数溢出,此时需要通过右规来调整尾数和阶码。右规时阶码加 1,需要判断是否发生了指数上溢。当调整前的阶码为 11111110 时,加 1 后,会变成 11111111 而发生指数上溢。

    左规:左规的阶码减 1,需要判断是否发生了指数下溢。其判断规则与指数上溢类似,左规一次,阶码减 1,然后判断阶码是否为全 0 来确定是否指数下溢。

    浮点数的溢出并不是以尾数溢出来判断的,尾数溢出可以通过右规操作得到纠正。运算结果是否溢出主要看结果的指数是否发生了上溢,因此是由指数上溢来判断的

    注意:某些题目可能会指定尾数或阶码采用补码表示。通常采用双符号位,当尾数求和结果溢出(如尾数为 10.xxxx01.xxxx)时,需右规一次;当结果出现 00.0xxxx11.1xxxx 时,需要左规,直到尾数变为 00.1xxxx11.0xxxx

    6. C 语言中的浮点数类型

    C 语言中的 floatdouble 类型分别对应于 IEEE 754 单精度浮点数和双精度浮点数。long double 类型应为扩展双精度浮点数,但 long double 的长度和格式随编译器和处理器类型的不同而异。

    1. int 转换为 float:虽然不会发生溢出,但 float 尾数连隐藏位共 24 位,当 int 型数的第 24~31 位非 0 时,无法精确转换成 24 位浮点数的尾数,需进行舍入处理,影响精度。
    2. int 或 float 转换为 double:因 double 的有效位数更多,因此能保留精确值。
    3. double 转换为 float:因 float 表示范围更小,大数转换时可能会发生溢出。此外,由于尾数有效位数变少,高精度数转换时会发生舍入。
    4. float 或 double 转换为 int:因 int 没有小数部分,数据会向 0 方向截断(仅保留整数部分),发生舍入。另外,因 int 表示范围更小,大数转换时可能会溢出。

    在不同数据类型之间转换时,往往隐藏着一些不容易察觉的错误,编程时要非常小心。

     

    四、数据校验

    408 不考,但是哈工程的 811 考