计组笔记第二章——数据的表示和运算

本章问题:

  • 数据如何在计算机中表示?
  • 运算器如何实现数据的算术、逻辑运算?

2.1.1 进位计数制

十进制计数法

  • 古印度人发明阿拉伯数字。
  • 基于乘法思想。
  • 十进制表示方式:
    整数情况下:

\[K_nK_{n-1}...K_2K_1K_0 = 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_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进制转十进制公式:

\[K_nK_{n-1}...K_2K_1K_0K_{-1}K_{-2}...K_{-m} = K_n \times r^n + K_{n-1} \times r^{n-1} + ... + K_2 \times r^2 + K_1 \times r^1 + K_0 \times r^0 + K_{-1} \times r^{-1} K_{-2} \times r^{-2} + ... + K_{-m} \times r^{-m} \]

  • 基数:每个数码位所用到的不同符号的个数,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芯片
    74181芯片是一个4位ALU,右侧的输入就是控制信号,用来控制当前的运算。

电路基础知识

  • 基本逻辑运算:
    当一个逻辑运算中同时出现&和|,先算与,再算或。
  • 复合逻辑:
    与非:A和B先与再非
    或非:A和B先或再非
    异或:可以用与、或符合而来,A、B只有一个为1是结果为1
    同或:异或取反

加法器的实现

一位全加器

  • 本位有两个参与运算的数\(A_i\)\(B_i\),还有低位进位\(C_{i-1}\), 本位的和记为\(S_i\)
  • 加法是一位一位的加,本位的输出结果&S_i&:输入中有奇数个1.

\[S_i = A_i \oplus B_i \oplus C_{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位补码数轴
    上图所示位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\)
    Booth算法举例

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.

  • 注:采用双符号位的好处是出现溢出还可以挽救。
  • 规格化浮点数的特点:
  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)\)
  2. 用补码表示的尾数进行规格化:
    正数和原码相同。
    负数:计算机规定,补码表示的负数尾数数值位的第一位必须是0,才算合法的规格化,其最大值表示为1.01...1; 最小值表示为1.00...0.

例:例题
解:将尾数算术左移3位,低位补0.
尾数变为1.0100000,左归了3次,阶码-3,变为0011.

  1. 浮点数的溢出:
    浮点数的溢出
    如果出现正下溢或负下溢的情况通常当作机器数的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标准的浮点数格式
  • 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表示整个数的正负
    阶码真值 = 移码 - 偏移量
    最终,这个浮点数的真值为:

\[(-1)^s \times 1.M \times 2^{E - 127} \]

  • 例:将十进制数-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 浮点数的运算

浮点数的加减运算

浮点数加减运算的步骤:

  1. 对阶:阶数小的向阶数大的转换(因为计算机内部,尾数是定点小数,如果阶大的转阶小的,尾数可能表示不下。)
  2. 尾数加减:将尾数进行相加
  3. 规格化:保证尾数是规格化的
  4. 舍入:由于浮点数的位数是有限的,超出有效部分的需要被舍弃。
  5. 判溢出:如果阶码的值超出规定范围,则判断溢出。
  • 例:已知十进制数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可能溢出及损失精度。
posted @ 2024-07-16 16:27  菜鸟小楠  阅读(17)  评论(0编辑  收藏  举报