0%

王道-计算机组成原理-ch2-数据的表示与运算

王道

数据的表示和运算

(一)数制与编码

进位计数制及其相互转换;真值和机器数;字符与字符串

(二)定点数的表示和运算
定点数的表示:无符号数的表示;有符号数的表示

定点数的运算:定点数的移位运算;原码定点数的加减运算;补码定点数的加减运算;定点数的乘/除运算;溢出概念和判别方法

(三)浮点数的表示和运算

浮点数的表示:IEEE 754标准;浮点数的加减运算

(四)算术逻辑单元(ALU)

串行加法器和并行加法器;ALU的功能和结构

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

1)在计算机中,为什么要采用二进制来表示数据?

2)计算机在字长足够的情况下能够精确地表示每个数吗?若不能,请举例说明。3)字长相同的情况下,浮点数和定点数的表示范围与精度有什么区别?

4)用移码表示浮点数的阶码有什么好处?

请读者在本章的学习过程中寻找答案,本章末尾会给出参考答案。

比较不清晰的知识点:小数的表示(IEEE 754和浮点数),和加减乘除(原补)

数制与编码

进位计数制及其相互转换

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

1)二进制只有两种状态,使用有两个稳定状态的物理器件就可以表示二进制数的每一位制造成本比较低,例如用高低电平或电荷的正负极性都可以很方便地表示0和1。

2)二进制位1和О正好与逻辑值“真”和“假”对应,为计算机实现逻辑运算和程序中的逻辑判断提供了便利条件。

3)二进制的编码和运算规则都很简单,通过逻辑门电路能方便地实现算术运算。

进位计数法

进位计数法是一种计数的方法。常用的进位计数法有十进制、二进制、八进制、十六进制等。十进制数是日常生活中最常使用的,而计算机中通常使用二进制数、八进制数和十六进制数。

在进位计数法中,每个数位所用到的不同数码的个数称为$\color{green}{\text{基数}}$。十进制的基数为10 (0~9),每个数位计满10就向高位进位,即“逢十进一”。

十进制数101,其个位的1显然与百位的1所表示的数值是不同的。每个数码所表示的数值等于该数码本身乘以一个与它所在数位有关的常数,这个常数称为$\color{green}{\text{位权}}$。一个进位数的数值大小就是它的各位数码按权相加。

一个$r$进制数($Kn K{n-1}\cdots K0 K{-1}\cdots K_{-m}$)的数值可表示为

$Knr^n + K{n-1}r^{n-1} + \cdots + K0r^0+\cdots + K{-m}r^{-m} = \displaystyle \sum_{i=n}^{-m}K_ir^i$

式中,$r$是基数;$r^i$是第$i$位的位权(整数位最低位规定为第$0$位);$K$的取值可以是$0, 1, \cdots ,r-1$共r个数码中的任意一个。

二进制

计算机中用得最多的是基数为2的计数制,即二进制。二进制只有0和1两种数字符号,计数“逢二进一”。它的任意数位的权为2,i为所在位数。

八进制

八进制作为二进制的一种书写形式,其基数为8,有0~7共8个不同的数字符号,计数“逢八进一”。因为r = 8 = $2^3$,所以只要把二进制中的3位数码编为一组就是一位八进制数码,两者之间的转换极为方便。

十六进制。

十六进制也是二进制的一种常用书写形式,其基数为16,“逢十六进一”。每个数位可取0~9、A、B、C、D、E、F中的任意一个,其中A、B、C、D、E、F分别表示10~15。因为r = 16=$2^4$,因此4位二进制数码与1位十六进制数码相对应。

不同进制数之间的相互转换
二进制数转换为八进制数和十六进制数

对于一个二进制混合数(既包含整数部分,又包含小数部分),在转换时应以小数点为界。其整数部分,从小数点开始往左数,将一串二进制数分为3位(八进制)一组或4位(十六进制)一组,在数的最左边可根据需要加“0”补齐;对于小数部分,从小数户开炬仕石效,也付一串二进制数分为3位一组或4位一组,在数的最右边也可根据需要加“0”补齐。最终使总的位数为3或4的整数倍,然后分别用对应的八进制数或十六进制数取代。

所以,对应的八进制数为$(1702.32)_8$= $(1111000010.01101)_2$。

同样,由八进制数或十六进制数转换成二进制数,只需将每位改为3位或4位二进制数即可(必要时去掉整数最高位或小数最低位的0)。八进制数和十六进制数之间的转换也能方便地实现,十六进数制转换为八进制数(或八进制数转换为十六进制数)时,先将十六进制(八进制)数转换为二进制数,然后由二进制数转换为八进制(十六进制)数较为方便。

任意进制数转换为十进制数

将任意进制数的各位数码与它们的权值相乘,再把乘积相加,就得到了一个十进制数。这种方法称为按权展开相加法。

例如,$(11011.1)_2= 1×2^4+1×2^3+0×2^2+1×2^1+1×2^0+1×2^{-1}=27.5。$

十进制数转换为任意进制数

一个十进制数转换为任意进制数,常采用基数乘除法。这种转换方法对十进制数的整数部分和小数部分将分别进行处理,对整数部分用除基取余法,对小数部分用乘基取整法,最后将整数部分与小数部分的转换结果拼接起来。

除基取余法(整数部分的转换):整数部分除基取余,最先取得的余数为数的最低位最后取得的余数为数的最高位(即除基取余,先余为低,后余为高),商为0时结束。

整数部分:

小数部分:

因此小数部分0.6875 =$(0.1011)_2$,所以123.6875=$(1111011.1011)_2$。

注意:在计算机中,小数和整数不一样,整数可以连续表示,但小数是离散的,所以并不是每个十进制小数都可以准确地用二进制表示。例如0.3,无论经过多少次乘二取整转换都无法得到精确的结果。但任意一个二进制小数都可以用十进制小数表示,希望读者引起重视。
注意:关于十进制数转换为任意进制数为何采用除基取余法和乘基取整法,以及所取之数放置位置的原理,请结合r进制数的数值表示公式思考,而不应死记硬背。

真值和机器数

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

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

*BCD码

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

8421码(最常用)

8421码(最常用)。它是一种有权码,设其各位的数值为b;,b,b, bo,则权值从高到低依次为8,4,2,1,它表示的十进制数为D= $8b_3,+ 4b_2+2b_1+ 1b_0$。如8→1000;9→1001。

若两个8421码相加之和小于等于$(1001)2$。即$(9){10}$,则不需要修正;若相加之和大于等于(1010),即(10)10,则要加6修正(从1010到1111这6个为无效码,当运算结果落于这个区间时,需要将运算结果加上 6),并向高位进位,进位可以在首次相加或修正时产生。

余3码。

这是一种无权码,是在8421码的基础上加$(0011)_2$形成的,因每个数都多余“3”,因此称为余3码。如8→1011;9→1100。

2421码。

这也是一种有权码,权值由高到低分别为2,4,2,1,特点是大于等于5的4位二进制数中最高位为1,小于5的最高位为0。如5→1011而非0101。

字符与字符串

由于计算机内部只能识别和处理二进制代码,所以字符都必须按照一定的规则用一组二进制编码来表示。

字符编码ASCII码

目前,国际上普遍采用的一种字符系统是7位二进制编码的 ASCII 码(每个字节的最高位保持为0,可用于传输时的奇偶校验位),它可表示10个十进制数码、52个英文大写字母和小写字母(A~Z,a~z)及一定数量的专用符号(如$、%、+、=等),共128个字符。

在 ASCII 码中,编码值0~31为控制字符,用于通信控制或设备的功能控制;编码值127是DEL码;编码值32是空格SP;编码值32~126共95个字符称为可印刷字符。

$\color{green}{\text{提示}}$:0~9的 ASCII码值为48(011 0000 ) ~57 (011 1001 ),即去掉高3位,只保留低4位,正好是二进制形式的0~9。
[65,90]=[A,Z],[97,122]=[a,z]

汉字的表示和编码

在1981年实施的国家标准GB 2312—1980 中,每个编码用两字节表示,收录了一级汉字3755个、二级汉字3008个、各种符号682个,共计7445个。

目前最新的汉字编码是2000年公布的国家标准GB 18030,它收录了27484个汉字。编码标准采用1B、2B和4B。

汉字的编码包括汉字的输入编码、汉字内码、汉字字形码三种,它们是计算机中用于输入、内部处理和输出三种用途的编码。区位码是国家标准局于1980年颁布、1981年实施的标准,它用两字节表示一个汉字,每字节用七位码,并将汉字和图形符号排列在一个94行94 列的二维代码表中。区位码是4位十进制数,前2位是区码,后2位是位码,所以称为区位码。

国标码将十进制的区位码转换为十六进制数后,再在每字节上加上 20H。国标码两字节的最高位都是0,ASCII 码的最高位也是0。为了方便计算机区分中文字符和英文字符,将国标码两字节的最高位都改为“1”,这就是汉字内码。

区位码和国标码都是输入码,它们和汉字内码的关系(十六进制)如下:

国标码=$\text{(区位码)}_{16}$+2020H

汉字内码=$\text{(国标码)}_{16}$+ 8080H

这一段话讲了和没讲是一样的

*校验码

校验码是指能够发现或能够自动纠正错误的数据编码,也称检错纠错编码。校验码的原理是通过增加一些冗余码,来检验或纠错编码。

通常某种编码都由许多码字构成,任意两个合法码字之间最少变化的二进制位数,称为数据校验码的$\color{green}{\text{码距}}$。对于码距不小于2的数据校验码,开始具有检错的能力。码距越大,检错、纠错的能力就越强,而且检错能力总是大于等于纠错能力。下面介绍3种常用的校验码(建议结合《计算机网络考研复习指导》第3章的“差错控制”章节进行综合复习)。

如1100和1101之间的码距为1,因为只有最低位翻转了。而1001和0010之间的码距则为3,因为只有1位没有变化。

奇偶校验

没什么好说的,只需注意奇偶数,中的奇偶指的是“1”的个数
码距为2,$\color{red}{\text{Q}}$:那么问题来了,其他校验方式的码距是多少

汉明码

按照书上的例子辅以参考文献记忆

把握一点:汉明码具有纠错一位的能力,检错两位
n为有效信息的位数,k为校验位的位数,n和k需要满足的关系:$n+k \leq 2^k-1$

汉明码的例子找不到图片(Image not found)
CRC循环冗余校验

按照书上的例子辅以参考文献记忆

需要把握的点

模2除法相当于做异或运算
余数的位数一定要是比除数位数只能少一位,哪怕前面位是0,甚至是全为0(附带好整除时)也都不能省略

CRC循环冗余校验的例子找不到图片(Image not found)

定点数的表示与运算

定点数的表示

无符号数和有符号数的表示

在计算机中参与运算的机器数有两大类:无符号数和有符号数。

1)无符号数。指整个机器字长的全部二进制位均为数值位,没有符号位,相当于数的绝对值。若机器字长为8位,则数的表示范围为0~$2^8-1$,即0~255。

2)有符号数。在机器中,数的“正”“负”号是无法识别的,有符号数用“O”表示“正”号,用“1”表示“负”号,从而将符号也数值化,并通常约定二进制数的最高位为符号位,即将符号位放在有效数字的前面,组成有符号数。

有符号数的机器表示有原码、补码、反码和移码。为了能正确区别真值和各种机器数,约定用X表示真值,用$\lbrack X \rbrack{\text{原}}$表示原码,$\lbrack X \rbrack{补}$表示补码,$\lbrack X \rbrack{反}$表示反码,$\lbrack X \rbrack{移}$表示移码。

机器数的定点表示

根据小数点的位置是否固定,在计算机中有两种数据格式:定点表示和浮点表示。本节仅介绍定点表示,浮点表示见2.3节。

定点表示即约定机器数中的小数点位置是固定不变的,小数点不再使用“.”表示,而是约定它的位置。理论上,小数点位置固定在哪一位都可以,但在计算机中通常采用两种简单的约定:将小数点的位置固定在数据的最高位之前,或固定在最低位之后。一般常称前者为定点小数,后者为定点整数。

定点小数

定点小数是$\color{green}{\text{纯小数}}$,约定小数点位置在符号位之后、有效数值部分最高位之前。若数据$X$的形式为$X=x_0.x_1x_2$(其中$x_0$为符号位,$x_1\backsim x_n$,是数值的有效部分,也称尾数,$x_1$为最高有效位),则在计算机中的表示形式如图2.4所示(设机器字长$n+1$位)。

当$x_0$=0,$x_1$~$x_n$均为1时,X为其所能表示的最大正数,真值等于$1-2^{-n}$。

当$x_0$=1,$x_1$~$x_n$,均为1时,X为其(原码)所能表示的最小负数,真值等于-$(1-2^{-n})$。

定点整数

定点整数是纯整数,约定小数点位置在有效数值部分最低位之后。若数据$X$的形式为$X=x_0x_1x_2 \cdots x_n$(其中$x_0$为符号位,$x_1 \backsim x_n$是尾数,$x_n$为最低有效位),则在计算机中的表示形式如图2.5所示(设机器字长n+1位)。

当$x_0=0, x_1 \backsim x_n$均为1是,$X$为其所能表示的最大正数,真值等于$2^n-1$。

当$x_0=1, x_1 \backsim x_n$均为1时,$X$为其(原码)所能表示的最小负数,真值等于$-(2^n-1)$

原码、补码、反码、移码
原码表示法

原码是一种比较简单、直观的机器数表示法,用机器数的最高位表示该数的符号,其余的各位表示数的绝对值。原码的定义如下。

  • 纯小数的原码定义

例如,若$x1=+0.1101,x2=-0.1101$字长为8位,则其原码表示为:$\lbrack x_1 \rbrack\text{原}$ = $\color{green}{\text{0}}$.1101000,$\lbrack x2 \rbrack\text{原}$ = $\color{green}{\text{1}}$.1101000,其中最高位为符号位,则其原码表示为:$\lbrack x1 \rbrack\text{原}$ = $\color{green}{\text{0}}$.1101000,$\lbrack x2 \rbrack\text{原}$ = $\color{green}{\text{1}}$.1101000

更一般地,对于正小数$x=+0.x1x_2\cdots x_n$,有$x\text{原}=0.x1x_2\cdots x_n$;对于负小数$x=-0.x_1x_2\cdots x_n$,有$x\text{原}=1.x_1x_2\cdots x_n$

若字长为$n+1$,则原码小数的表示范围为$-(1-2^{-n}) \leq x \leq 1-2^{-n}$(关于原点对称)

  • 纯整数的原码定义

例如,若$x1=+1110,x2=-1110$字长为8位,则其原码表示为:$\lbrack x_1 \rbrack\text{原}$ = $\color{green}{\text{0}}$,0001110,$\lbrack x2 \rbrack\text{原}$ =$2^7 + 1110$ =$\color{green}{\text{1}}$,0001110,其中最高位为符号位

若字长为$n+1$,则原码小数的表示范围为$-(2^{n} -1) \leq x \leq 2^{n} -1$(关于原点对称)

真值零的原码表示有正零和负零两种形式,即$\lbrack +0 \rbrack\text{原}=\color{green}{\text{0}}0000$和$\lbrack -0 \rbrack\text{原}=\color{green}{\text{1}}0000$。

补码表示法

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

  • 纯小数的补码定义

例如,若$x1=+0.1001,x2=-0.0110$字长为8位,则其补码表示为:$\lbrack x_1 \rbrack\text{补}$ = $\color{green}{\text{0}}$.1001000,$\lbrack x2 \rbrack\text{补}$ = 2- 0.0110= $\color{green}{\text{1}}$.1010000,其中最高位为符号位

更一般地,对于正小数$x=+0.x1x_2\cdots x_n$,有$x\text{补}=0.x1x_2\cdots x_n$;对于负小数$x=-0.x_1x_2\cdots x_n$,有$x\text{补}=10.00\cdots0 - 0.x_1x_2\cdots x_n$(mod 2)

这里的mod2是什么意思?

若字长为$n+1$,则补码小数的表示范围为$-1 \leq x \leq 1-2^{-n}$(比原码多表示-1)

  • 纯整数的补码定义

例如,若$x1=+1010,x2=-1101$字长为8位,则其补码表示为:$\lbrack x_1 \rbrack\text{补}$ = $\color{green}{\text{0}}$,0001010,$\lbrack x2 \rbrack\text{补}$ =$2^8 - 0,0001101$ =$\color{green}{\text{1}}$,1110011。

若字长为$n+1$,则补码小数的表示范围为$-2^{n} \leq x \leq 2^{n} -1$(比原码多表示$-2^n$)

真值零的补码表示是唯一的。即$\lbrack +0 \rbrack\text{补} = \lbrack -0 \rbrack\text{补}=0.0000$,由定义$\lbrack +0 \rbrack_\text{补} = 10.0000 - 1.0000 = 1.0000$,可见对于小数,补码比原码多表示一个“-1”.类似地,对于整数,补码比原码多表示一个“$-2^n$”

  • 由原码求补码、由补码求原码

对于正数,补码与原码的表示相同,$\lbrack x \rbrack\text{补} = \lbrack x \rbrack\text{原}$。
对于负数,原码符号位不变,数值部分按位取反,末位加1(即所谓“取反加1”),此规则同样适用于由$\lbrack x \rbrack\text{补}$求$\lbrack x \rbrack\text{原}$

  • 补码的算术移位

将$\lbrack x \rbrack_\text{补}$的符号位与数值位一起右移一位并保持原符号位的值不变,可实现除法功能(除以2)。

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

模4补码双符号位00表示正,11表示负,用在完成算术运算的ALU部件中。

反码表示法

反码通常用来作为由原码求补码或由补码求原码的中间过渡。

  • 纯小数的补码定义

例如,若$x1=+0.0110,x2=-0.0110$字长为8位,则其反码表示为:$\lbrack x_1 \rbrack\text{反}$ = $\color{green}{\text{0}}$.0110000,$\lbrack x2 \rbrack\text{反}$ = 1.1111111- 0.0110000= $\color{green}{\text{1}}$.100111。

若字长为$n+1$,则反码小数的表示范围为$-(1-2^{-n}) \leq x \leq 1-2^{-n}$(关于原点对称)。

真值零的反码表示不唯一,负数的反码符号位为“1”,数值部分求反,$\lbrack +0 \rbrack\text{反}$ = 0.0000;$\lbrack -0 \rbrack\text{反}$ = 1.1111。

  • 纯整数的反码定义

例如,若$x1=+1011,x2=-1011$字长为8位,则其反码表示为:$\lbrack x_1 \rbrack\text{反}$ = $\color{green}{\text{0}}$,0001011,$\lbrack x2 \rbrack\text{反}$ =$1,1111111 - 0,0001011$ =$\color{green}{\text{1}}$,1110100。

若字长为$n+1$,则反码小数的表示范围为$-(2^{n} - 1) \leq x \leq 2^{n} -1$(关于原点对称))

真值、原码、补码、反码及$\lbrack -x \rbrack_\text{反}$的转换规律,如图2.6所示。

移码表示法

移码常用来表示浮点数的$\color{green}{\text{阶码}}$。它只能表示$\color{green}{\text{整数}}$。

移码就是在真值$X$上加上一个常数(偏置值),通常这个常数取$2^n$,相当于$X$在数轴上向正方向偏移了若干单位,这就是“移码”一词的由来。移码定义为

例如,若整数$x1 = +10101,x_2=-10101$,字长为8位,则其移码表示为:$\lbrack x_1 \rbrack\text{移}=2^7 + 10101 = 1,0010101$;$\lbrack x1 \rbrack\text{移}=2^7+ (-10101)=0,1101011$

$\color{green}{\text{移码具有以下特点}}$:

  • 移码中零的表示唯一,$\lbrack +0 \rbrack\text{移} = 2^n + 0 = \lbrack -0 \rbrack\text{移} = 2^n-0=100\cdots0$($n$个“0”)
  • 一个真值的$\color{green}{\text{移码}}$和$\color{green}{\text{补码}}$仅差一个符号位,$\lbrack +0 \rbrack\text{补}$的符号位取反即得$\lbrack +0 \rbrack\text{移}$(“1”表示正,“0”表示负,这与其他机器数的符号位取值正好相反),反之亦然
  • 移码全为0时,对应真值的最小值为$-2^n$;移码全为1时,对应真值的最大值$2^n01$。
  • 移码保持了数据原有的大小顺序,移码大真值就大,移码小真值就小

定点数的运算

定点数的移位运算

移位运算根据操作对象的不同分为算术移位和逻辑移位。有符号数的移位称为算术移位,逻辑移位的操作对象是逻辑代码,可视为无符号数。

算术移位

算术移位的对象是有符号数,在移位过程中$\color{green}{\text{符号位}}$ $\color{green}{\text{保持不变}}$ 。

对于正数,由于$\lbrack x \rbrack\text{原}$ = $\lbrack x \rbrack\text{补}$ = $\lbrack x \rbrack_\text{反}$ = 真值,因此移位后出现的空位均以0添之。对于负数,由与原码,补码,反码的表示形式不同,因此当机器数移位时,对其空位的添补规则也不同

不论是正数还是负数,移位后其符号位均不变,且移位后都相当于对真值补0,根据补码、反码的特性,所以在负数时填补代码有区别。

对于原码,左移一位若不产生溢出,相当于乘以2(与十进制的左移一位相当于乘以10类似),右移一位,若不考虑因移出而舍去的末位尾数,相当于除以2。

由表2.1可以得出如下结论。

正数的原码、补码与反码都相同,因此移位后出现的空位均以0 添之。对于负数,由于原码、补码和反码的表示形式不同,因此当机器数移位时,对其空位的添补规则也不同。

  • 负数的原码数值部分与真值相同,因此在移位时只要使符号位不变,其空位均添0。
  • 负数的反码各位除符号位外与负数的原码正好相反,因此移位后所添的代码应与原码相反,即全部添1。
  • 分析由原码得到补码的过程发现,当对其由低位向高位找到第一个“1”时,在此“1”左边的各位均与对应的反码相同,而在此“1”右边的各位(包括此“1”在内)均与对应的原码相同。因此负数的补码左移时,因空位出现在低位,则添补的代码与原码相同,即添0;右移时因空位出现在高位,则添补的代码应与反码相同,即添1。
逻辑移位

逻辑移位将操作数视为无符号数,移位规则:逻辑左移时,高位移丢,低位添0;逻辑右移时,低位移丢,高位添0。

逻辑移位不管是左移还是右移,都添0。

循环移位分为带进位标志位CF的循环移位(大循环)和不带进位标志位的循环移位(小循环),过程如图2.7所示。

循环移位的主要特点是,移出的数位又被移入数据中,而是否带进位则要看是否将进位标志位加入循环位移。例如,带进位位的循环左移〔见图2.7(d)]就是数据位连同进位标志位一起左移,数据的最高位移入进位标志位CF,而进位位则依次移入数据的最低位。

循环移位操作特别适合将数据的低字节数据和高字节数据互换。

原码定点数的加减法运算

设$\lbrack X \rbrack\text{原}= x_s.x_1x_2\cdots x_n$和$\lbrack Y \rbrack\text{原}= y_s.y_1y_2\cdots y_n$,进行加减运算的规则如下。

$\color{green}{\text{加法规则}}$:先判符号位,若相同,则绝对值相加,结果符号位不变;若不同,则做减法,绝对值大的数减去绝对值小的数,结果符号位与绝对值大的数相同。

$\color{green}{\text{减法规则}}$:两个原码表示的数相减,首先将减数符号取反,然后将被减数与符号取反后的减数按原码加法进行运算。

运算时注意机器字长,当左边位出现溢出时,将溢出位丢掉。

补码定点数加减法运算

补码加减运算规则简单,易于实现,因此计算机系统中普遍采用补码加减运算。补码运算的特点如下(设机器字长为$n+1$)。

1)参与运算的两个操作数均用补码表示。2)按二进制运算规则运算,逢二进一。

3)符号位与数值位按同样规则一起参与运算,符号位运算产生的进位要丢掉,结果的符号
位由运算得出。

4)补码加减运算依据下面的公式进行。当参加运算的数是定点小数时,模M= 2;当参加
运算的数是定点整数时,模$M=2^n+ 1$。

mod M运算是为了将溢出位丢掉。

也就是说,若做加法,则两数的补码直接相加;若做减法,则将被减数与减数的机器负数相加。

补码运算的结果亦为补码

设机器字长为8位(含1位符号位),A= 15,B=24,求$\lbrack A+B \rbrack\text{补}$和$\lbrack A-B \rbrack\text{补}$。

解:

A=+15=+0001111,B=+24=+0011000;得$\lbrack A \rbrack\text{补}$= 00001111,$\lbrack B \rbrack\text{补}$=00011000。

求得$\lbrack -B \rbrack_\text{补}$ = 11101000。所以

$\lbrack A+B \rbrack_\text{补}$=00001111+ 00011000 = 00100111,其符号位为0,对应真值为+39。

$\lbrack A-B \rbrack\text{补}$=$\lbrack A \rbrack\text{补} + \lbrack - B \rbrack_\text{补}$=00001111 + 11101000= 11110111,其符号位为1,对应真值为-9。

符号扩展

在计算机算术运算中,有时必须把采用给定位数表示的数转换成具有不同位数的某种表示形式。例如,某个程序需要将一个8位数与另外一个32位数相加,要想得到正确的结果,在将8位数与32位数相加之前,必须将8位数转换成32位数形式,这称为“符号扩展”。

正数的符号扩展非常简单,即原有形式的符号位移动到新形式的符号位上,新表示形式的所有附加位都用0进行填充。

负数的符号扩展方法则根据机器数的不同而不同。原码表示负数的符号扩展方法与正数相同,只不过此时符号位为1。补码表示负数的符号扩展方法:原有形式的符号位移动到新形式的符号位上,新表示形式的所有附加位都用1(对于整数)或0(对于小数)进行填充。反码表示负数的符号扩展方法:原有形式的符号位移动到新形式的符号位上,新表示形式的所有附加位都用1进行填充。

正数相当于位数往左边扩展,负数相当于位数往右边扩展

溢出概念和判别方法

溢出是指运算结果超过了数的表示范围。通常,称大于机器所能表示的最大正数为上溢,称小于机器所能表示的最小负数为下溢。定点小数的表示范围为$\lvert x \rvert <1$,如图2.8所示。

仅当两个符号相同的数相加或两个符号相异的数相减才可能产生溢出,如两个正数相加,而结果的符号位却为1(结果为负);一个负数减去一个正数,结果的符号位却为0(结果为正)。定点数加减运算出现溢出时,运算结果是错误的。

补码定点数加减运算溢出判断的方法有3种。

(1)采用一位符号位

由于减法运算在机器中是用加法器实现的,因此无论是加法还是减法,只要参加操作的两个数符号相同,结果又与原操作数符号不同,则表示结果溢出。

设A的符号为$A_s$,B的符号为$B_s$,运算结果的符号为$S_s$,则溢出逻辑表达式为

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

(2)采用双符号位

双符号位法也称模4补码。运算结果的两个符号位$S{s1}S{s2}$相同,表示未溢出;运算结果的两个符号位$S{s1}S{s2}$不同,表示溢出,此时最高位符号位代表真正的符号。

符号位$S{s1}S{s2}$的各种情况如下:

  • $S{s1}S{s2}=00$:表示结果为正数,无溢出。
  • $S{s1}S{s2}=01$:表示结果正溢出。
  • $S{s1}S{s2}=10$:表示结果负溢出。
  • $S{s1}S{s2}=11$:表示结果为负数,无溢出。

Q:运算数的符号位在双符号位中表示是怎么样的?
A:正数00,负数11

溢出逻辑判断表达式为$V=S{s1}\oplus S{s2}$,若$V=0$,表示无溢出;若$V=1$表示有溢出。

(3)采用一位符号位根据数据位的进位情况判断溢出

若符号位的进位$C_s$与最高数位的进位$C_1$相同,则说明没有溢出,否则表示发生溢出。溢出逻辑判断表达式为V=$C_s\oplus C_1$,若V=0,表示无溢出;V=1,表示有溢出。

这是在干什么??

定点数的乘法运算

在计算机中,乘法运算由累加和右移操作实现。根据机器数的不同,可分为原码一位乘法和补码一位乘法。原码一位乘法的规则比补码一位乘法简单。

原码一位乘法

原码一位乘法的特点是符号位与数值位是分开求的,乘积符号由两个数的符号位“异或”形成,而乘积的数值部分则是两个数的绝对值相乘之积。

设$\lbrack X \rbrack\text{原}= x_s.x_1x_2\cdots x_n$和$\lbrack Y \rbrack\text{原}= y_s.y_1y_2\cdots y_n$,则运算规则如下:

  • ${\textstyle\unicode{x2460}}$ 被乘数和乘数均取绝对值参加运算,符号位为$x_s \oplus y_s$
  • ${\textstyle\unicode{x2461}}$ 部分积的长度同被乘数,取$n +1$位,以便存放乘法过程中绝对值大于等于1的值初值为0。
  • ${\textstyle\unicode{x2462}}$ 从乘数的最低位$y_n$,开始判断:若$y_n = 1$,则部分积加上被乘数$\lvert x \rvert$,然后右移一位;若$y_n$= 0,则部分积加上0,然后右移一位。
  • ${\textstyle\unicode{x2463}}$ 重复步骤${\textstyle\unicode{x2462}}$,判断$n$次

由于乘积的数值部分是两数绝对值相乘的结果,因此原码一位乘法运算过程中的右移操作均为逻辑右移。

注意:考虑到运算时可能出现绝对值大于1的情况(但此刻并非溢出),所以部分积和被乘数取$\color{green}{\text{双符号位}}$。

$\color{red}{\text{例}}$:设机器字长为5位(含1位符号位,n=4),x = —0.1101,y= 0.1011,采用原码一位乘法求x · y。

补码一位乘法(Booth 算法)

这是一种有符号数的乘法,采用相加和相减操作计算补码数据的乘积。

设$\lbrack X \rbrack\text{补}= x_s.x_1x_2\cdots x_n$和$\lbrack Y \rbrack\text{补}= y_s.y_1y_2\cdots y_n$,则运算规则如下:

  • ${\textstyle\unicode{x2460}}$ 符号位参与运算,运算的数均以补码表示。
  • ${\textstyle\unicode{x2461}}$ 被乘数一般取双符号位参与运算,部分积取双符号位,初值为0,乘数可取单符号位。
  • ${\textstyle\unicode{x2462}}$ 乘数末位增设附加位.$y_{n+1}$,且初值为0。
  • ${\textstyle\unicode{x2463}}$ 根据$(yn,y{n+1})$的取值来确定操作,见表2.2。
  • ${\textstyle\unicode{x2464}}$ 移位按补码右移规则进行。
  • ${\textstyle\unicode{x2465}}$ 按照上述算法进行$n+1$步操作,但第$n+1$步不再移位(共进行$n+1$次累加和$n$次右移),仅根据$yn$,与$y{n+1}$的比较结果做相应的运算。

【例2.8】设机器字长为5位(含1位符号位,n = 4),x =—0.1101,y = 0.1011,采用Booth算法求x· y。

定点数的除法运算

在计算机中,除法运算可转换成“累加一左移”(逻辑左移),根据机器数的不同,可分为原码除法和补码除法。

如果恢复余数法,先减去这个除数,在判断够不够减,不够减需要恢复之前的余数,所以叫恢复余数法
不恢复余数法,会比恢复余数法效率高,(少了恢复余数的步骤
详见天勤视频

怎么判断一个运算是补码的运算还是原码的运算,只要看符号位参不参与运算,符号位参与运算那么就是补码运算;
符号位不参与运算那么就是原码运算,此时结果的符号位由运算数单独运算得到

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

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

设被除数$\lbrack X \rbrack\text{原}= x_s.x_1x_2\cdots x_n$和$\lbrack Y \rbrack\text{原}= y_s.y_1y_2\cdots y_n$,则

  • ${\textstyle\unicode{x2460}}$ 商的符号:$Q_s = x_s \oplus y_s$
  • ${\textstyle\unicode{x2461}}$ 商的数值:$\lvert Q \rvert = \lvert X \rvert / \lvert Y \rvert$

求$\lvert Q \rvert$的不恢复余数法运算规则如下。

  • ${\textstyle\unicode{x2460}}$ 符号位不参与运算。
  • ${\textstyle\unicode{x2461}}$ 先用被除数减去除数$\lvert X \rvert - \lvert Y \rvert = \lvert X \rvert + (- \lvert Y \rvert) = \lvert X \rvert + \lbrack -Y \rbrack_\text{补}$,当余数为正时,商上1,余数和商左移一位,再减去除数;当余数为负时,商上0,余数和商左移一位,再加上除数。
  • ${\textstyle\unicode{x2462}}$ 当第$n+1$步余数为负时,需加上$\lvert Y \rvert$得到第n+1步正确的余数(余数和被除数同号)

数据都用补码表示,符号位参与运算;
初始时观察被除数与除数符号,同号做减法,异号做加法;
若余数与除数同号,则商1后余数左移一位减去除数,若余数与除数异号,则商0后余数左移一位加上除数;
重复上一步操作,直到得到n位商(n为数据位位数);
一般在末尾补上一个1。
摘自天勤

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

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

${\textstyle\unicode{x2460}}$ 符号位参加运算,除数与被除数均用补码表示,商和余数也用补码表示。

${\textstyle\unicode{x2461}}$ 若被除数与除数同号,则被除数减去除数;若被除数与除数异号,则被除数加上除数。

${\textstyle\unicode{x2462}}$ 若余数与除数同号,则商上 1,余数左移一位减去除数;若余数与除数异号,则商上0,余数左移一位加上除数。

${\textstyle\unicode{x2463}}$ 重复执行第③步操作n次。

${\textstyle\unicode{x2464}}$ 若对商的精度没有特殊要求,则一般采用“末位恒置1”法。

(3)除法运算总结除法运算总结

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

统考大纲要求考生具有对高级程序设计语言(如C语言)中相关问题进行分析的能力,而C语言变量之间的类型转换是统考中经常出现的题目,需要读者深入掌握这一内容.

short 16位
char 8 位(一般规定一个字节8位)

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

C语言允许在不同的数据类型之间做强制类型转换,而从数学的角度来说,可以想到很多不同的转换规则。就用户使用而言,对于两者都能表示的数,当然希望转换过程中数值本身不发生任何变化,而那些转换过后无法表示的数呢?请先观察如下这段程序:

1
2
3
4
5
int main(){
short x=-4321;
unsigned short y=(unsigned short)x;
printf ("x=%d, y=%u\n", x,y);
}

有符号数x是一个负数,而无符号数y的表示范围显然不包括x的值。读者可以自己猜想一下这段程序的运行结果,再比较下面给出的运行结果。

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

最后的结果中,得到的y值似乎与原来的x没有一点关系。不过将这两个数化为二进制表示时,我们就会发现其中的规律,如表2.5所示。

其中x为补码表示,y为无符号的二进制真值。观察可知,将short int强制转换为unsigned short只改变数值,而两个变量对应的每位都是一样的。通过这个例子就可以知道强制类型转换的结果保持位值不变,仅改变了解释这些位的方式。

下面再来看一个unsigned short型转换到short型的例子。考虑如下代码;

1
2
3
4
5
int main(){
unsigned short x=65535;
short y=(short) x;
printf("x=%u,y=%d \n", x, y);
}

同样在采用补码的机器上,上述代码会输出以下结果:

同样可以把这两个数用之前的方法写成二进制,然后证实我们之前得出的结论。

不同字长整数之间的转换

另一种常见的运算是在不同字长的整数之间进行数值转换。先观察如下程序:

1
2
3
4
5
6
int main(){
int x=165537,u=-34991;//int型占用4B
short y=(short)x, v=(short)u; // short型占用2B
printf("x=%d, y=%d\n", x, y);
printf ("u=%d, v=%d\n",u, v);}
}

这段程序可以得到如下结果:

x=165537,y =-31071

u=-34991, v= 30545

其中x、y、u、v的十六进制表示分别为0x000286a1、0x86a1、0xff7751、0x7751,观察上述数字很容易得出结论,当大字长变量向小字长变量强制类型转换时,系统把多余的高位字长部分直接截断,低位直接赋值,因此也是一种保持位值的处理方法。

最后来看小字长变量向大字长变量转换的情况。先观察下面这段程序:

1
2
3
4
5
6
7
8
int main(){
short x=-4321;
int y=x;
unsigned short u= (unsigned short)x;
unsigned int v=u;
printf("x-8d, y-%dln", x,y);printf ("u=%u,v=%u\n",u, v);
}

运行结果如下:

x=-4321,y =—4321

u=61215, v= 61215

x、y、u、v的十六进制表示分别是0xef1f、0xfffef1f、0xef1f、0x0000eflf,由本例可知短字长整数到长字长整数的转换,不仅要使相应的位值相等,高位部分还会扩展为原数字的符号位,这与之前的三个举例都不一样,从位值与数值的角度说,前三个举例的转换规则都是保证相应的位值相等,而短字长到长字长的转换,在位值相等的条件下还要补充高位的符号位,可以理解为数值的相等。注意,char类型为8位ASCII码整数,其转换为int时,在高位部分补0即可。

数据的存储和排列

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

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

现代计算机基本上都采用字节编址,即每个地址编号中存放1字节。不同类型的数据占用的字节数不同,int和 float型数据占4字节,double型数据占8字节等,而程序中对每个数据只给定一个地址。假设变量i的地址为80 00H,字节01H、23H、45H、67H应该各有一个内存地址,那么地址08 00H对应4字节中哪字节的地址呢?这就是字节排列顺序问题。

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

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

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

4004d3:01 05 64 94 04 08 add %eax,0x8049464

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

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

假设存储字长为32位,可按字节、半字和字寻址。对于机器字长为32位的计算机,数据以边界对齐方式存放,半字地址一定是2的整数倍,字地址一定是4的整数倍,这样无论所取的数据是字节、半字还是字,均可一次放存取出。所存储的数据不满足上述要求时,通过填充空白字节使其符合要求。这样虽然浪费了一些存储空间,但可提高取指令和取数的速度。

数据不按边界对齐方式存储时,可以充分利用存储空间,但半字长或字长的指令可能会存储在两个存储字中,此时需要两次访存,并且对高低字节的位置进行调整、连接之后才能得到所要的指令或数据,从而影响了指令的执行效率。

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

边界对齐方式相对边界不对齐方式是一种空间换时间的思想。RISC如ARM 采用边界对齐方式,而CISC如x86对齐和不对齐都支持。因为对齐方式取指令时间相同,因此能适应指令流水。

浮点数的表示与运算

补码运算的特点是符号位参与运算;原码运算的特点是,符号位先异或计算得出

浮点数的表示

浮点数表示法是指以适当的形式将比例因子表示在数据中,让小数点的位置根据需要而浮动。这样,在位数有限的情况下,既扩大了数的表示范围,又保持了数的有效精度。例如,用定点数表示电子的质量($9×10^{-28}$g)或太阳的质量($2×10^{33}g$)是非常不方便的。

浮点数的表示格式

通常,浮点数表示为

式中,$r$时浮点数阶码的底,(隐含),与尾数的基数相同,通常$r=2$。$E$和$M$都是有符号的定点数,$E$称为阶码,$M$称为位数。可见浮点数由阶码和尾数两部分组成,如图2.12所示

阶码是整数,阶符$J_f$和阶码的位数$m$共同反映浮点数的表示范围及小数点的实际位置;数符$S_f$代表浮点数的符号;尾数的位数$n$反映浮点数的精度。

规格化浮点数

为了提高运算的精度,需要充分地利用尾数的有效数位,通常采取浮点数规格化形式,即规定尾数的最高数位必须是一个有效值。非规格化浮点数需要进行规格化操作才能变成规格化浮点数。所谓规格化操作,是指通过调整一个非规格化浮点数的尾数和阶码的大小,使非零的浮点数在尾数的最高数位上保证是一个有效值。

左规:当浮点数运算的结果为非规格化时要进行规格化处理,将尾数算术左移一位、阶码减1(基数为2时)的方法称为左规,左归可能要进行多次。

右规:当浮点数运算的结果尾数出现溢出(双符号位为01或10)时,将尾数算术右移一位、阶码加1(基数为2时)的方法称为右规。需要右归时,只需进行一次。

规格化浮点数的尾数$M$的绝对值应满足条件$1/r \leq \lvert M \rvert \leq 1$。

1)原码规格化后。

正数为$0.1xx\cdots x$的形式,其最大值表示为$0.11\cdots1$,最小是表示为$0.100\cdots0$。

尾数表示的范围为$1/2 \leq M \leq (1-2^{-n})$。

负数为$1.1xx\cdots x$的形式,其最大值表示为$1.10\cdots0$,最小值表示为$1.11\cdots1$。

尾数的表示范围为$-(1 -2^{-n}) \leq M \leq -1/2$

2)原码规格化后。

正数为$0.1xx\cdots x$的形式,其最大值表示为$0.11\cdots1$,最小是表示为$0.100\cdots0$。

尾数表示的范围为$1/2 \leq M \leq (1-2^{-n})$。

负数为$1.0xx\cdots x$的形式,其最大值表示为$1.01\cdots1$,最小值表示为$1.00\cdots0$。

尾数的表示范围为$-1 \leq M \leq -(1/2 + 2^{-n})$

这里补码规格化尾数的最大负数形式为 $1.01\cdots1$,而不是原码的形式$1.10\cdots0$,因为$1.10\cdots0$不是补码规格化数,所以规格化尾数的最大负数时$-(0.10\cdots0 + 0.0\cdots01) = -0.10\cdots01,$而$(-0.10\cdots01)_\text{补}=1.01\cdots1$。
为什么$\dfrac{1}{2}$不是规格化浮点数:参考文献

当浮点数尾数的基数为2时,原码规格化数的尾数最高位一定是1,补码规格化数的尾数最高位一定与尾数符号位相反。基数不同,浮点数的规格化形式也不同。当基数为4时,原码规格化形式的尾数最高两位不全为0;当基数为8时,原码规格化形式的尾数最高3位不全为0。

*浮点数的表示范围

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

IEEE 754标准

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

IEEE 754标准规定常用的浮点数格式有短浮点数(单精度、float型)、长浮点数(双精度、double型)、临时浮点数,见表2.6。

IEEE 754标准的浮点数(除临时浮点数外),是尾数用采取隐藏位策略的原码表示,且阶码用移码表示的浮点数。

以短浮点数为例,最高位为数符位;其后是8位阶码,以2为底,用移码表示,阶码的偏置值为$2^{8-1}= 1$ = 127;其后23位是原码表示的尾数数值位。对于规格化的二进制浮点数,数值的最高位总是“1”,为了能使尾数多表示一位有效位,将这个“1”隐含,因此尾数数值实际上是24位。隐含的“1”是一位整数。在浮点格式中表示的 23位尾数是纯小数。例如,$(12)_{10}$ =$(1100)_2$,将它规格化后结果为$1.1×2^3$,其中整数部分的“1”将不存储在23位尾数内。

  • IEEE 754数值位永远是原码表示
  • 这里应该是说错了,规格化浮点数的结果不可能是$1.1×2^3$,只能说编者思路不清晰,可以理解为IEEE 754数值永远是1.xxx

短浮点数与长浮点数都采用隐含尾数最高数位的方法,因此可多表示一位尾数。临时浮点数又称扩展精度浮点数,无隐含位。

阶码是以移码形式存储的。对于短浮点数,偏置值为127;对于长浮点数,偏置值为1023。存储浮点数阶码部分之前,偏置值要先加到阶码真值上。上例中,阶码值为3,因此在短浮点数中,移码表示的阶码为127+3=130(82H);在长浮点数中,阶码为1023+3=1026(402H)。

IEEE 754标准中,规格化的短浮点数的真值为

规格化长浮点数的真值为

式中,$s =0$表示正数,$s =1$表示负数;短浮点数E的取值为$1\backsim254$(8位表示),$M$为23位,共32位;长浮点数E的取值为1~2046 (11位表示),M为52位,共64位。IEEE 754标准浮点数的范围见表2.7。

偏置值为127(而非 128〉时,空出8位全1来表示无穷大(若偏置值选128,则不能区分无穷大)。此外,阶码值E的范围为1~254,空出全0表示非规格化数。
由此需要注意,用传统的移码规则计算阶码所得出来的真值,不是正确的阶码真值(正确的做法是当无符号数来计算,然后-127)
注意 IEEE 754 形式分为 $\color{green}{\text{规格化浮点数}}$ 和 $\color{green}{\text{非规格化浮点数}}$
阶码全为0时为非规格化浮点数,对于非规格化数阶码部分是有效的(即-127),但是尾数没有隐含1

IEEE 754单精度浮点数的各种极值情况( 百度百科)找不到图片(Image not found)

IEEE754标准解析

char -> int -> long -> double , 类型转换

定点、浮点表示的区别

(1)数值的表示范围

若定点数和浮点数的字长相同,则浮点表示法所能表示的数值范围将远远大于定点表示法。

(2)精度

所谓精度,是指一个数所含有效数值位的位数。对于字长相同的定点数和浮点数来说,浮点数虽然扩大了数的表示范围,但精度降低了。

(3)数的运算

浮点数包括阶码和尾数两部分,运算时不仅要做尾数的运算,还要做阶码的运算,而且运算结果要求规格化,所以浮点运算比定点运算复杂。

(4)溢出问题

在定点运算中,当运算结果超出数的表示范围时,发生溢出;浮点运算中,运算结果超出尾数表示范围却不一定溢出,只有规格化后阶码超出所能表示的范围时,才发生溢出。

浮点数的加减运算

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

Q:如何判断浮点数溢出?
A:阶码溢出

对阶

对阶的目的是使两个操作数的小数点位置对齐,即使得两个数的阶码相等。为此,先求阶差,然后以小阶向大阶看齐的原则,将阶码小的尾数右移一位(基数为2),阶加1,直到两个数的阶码相等为止。尾数右移时,舍弃掉有效位会产生误差,影响精度。

尾数求和

将对阶后的尾数按定点数加(减)运算规则运算。

规格化

以双符号位为例,当尾数大于0时,其补码规格化形式为

当尾数小于0时,其补码规格化形式为

可见,当尾数的最高数值位与符号位不同时,即为规格化形式。规格化分为左规与右规两种。

1)左规:当尾数出现$00.0xx\cdots x$或$11.1xx\cdots x$时,需左规,即尾数左移1位,和的阶码减1,直到尾数为$00.1××\cdots ×$或$11.0××\cdots ×$。

2)右规:当尾数求和结果溢出(如尾数为$10.xx \cdots x$或$01.xx\cdots x$)时,需右规,即尾数右移一位,和的阶码加1。

1)对于左规和右规,不应死记。考查尾数的大小,左规一次相当于乘2,右规一次相当于除2;2)$\lbrack -1/2 \rbrack\text{补}$= 1.1000不是规格化数,需左规一次,$\lbrack -1\rbrack\text{补}$= 1.0000才是规格化数。

  • 讲IEEE 754跟补码有什么关系。?内容编排又出问题了
舍入

在对阶和右规的过程中,可能会将尾数低位丢失,引起误差,影响精度。常见的舍入方法有:“0”舍“1”入法和恒置“1”法。

“0”舍“1”入法:类似于十进制数运算中的“四舍五入”法,即在尾数右移时,被移去的最高数值位为0,则舍去;被移去的最高数值位为1,则在尾数的末位加1。这样做可能会使尾数又溢出,此时需再做一次右规。

恒置“1”法:尾数右移时,不论丢掉的最高数值位是“1”还是“O”,都使右移后的尾数末位恒置“1”。这种方法同样有使尾数变大和变小的两种可能。

溢出判断

与定点数加减法一样,浮点数加减运算最后一步也需判断溢出。

在浮点数规格化中已指出,当尾数之和(差)出现01.×××或10.xxx时,并不表示溢出,只能将此数右规后,再根据阶码来判断浮点数运算结果是否溢出。

浮点数的溢出与否是由阶码的符号决定的。以双符号位补码为例,当阶码的符号位出现“01”时,即阶码大于最大阶码时,表示上溢,进入中断处理;当阶码的符号位出现“10”时,即阶码小于最小阶码时,表示下溢,按机器零处理。实际上原理还是阶码符号位不同表示溢出,且真实符号位和高位符号位一致。

C语言中的浮点数类型及类型转换

C语言中的 float和 double类型分别对应于IEEE 754单精度浮点数和双精度浮点数。longdouble类型对应于扩展双精度浮点数,但long double 的长度和格式随编译器和处理器类型的不同而有所不同。在C程序中等式的赋值和判断中会出现强制类型转换,以char→int→long→double和 float→double最为常见,从前到后范围和精度都从小到大,转换过程没有损失。

1)从int转换为float时,虽然不会发生溢出,但 int可以保留32位,float保留24位,可能有数据舍入,若从int转换为double则不会出现。

2)从int或float转换为double时,因为double的有效位数更多,因此能保留精确值。3)从double转换为float时,因为float 表示范围更小,因此可能发生溢出。此外,由于有效位数变少,因此可能被舍入。

4)从float或double转换为 int 时,因为int没有小数部分,所以数据可能会向0方向被截断(仅保留整数部分),影响精度。另外,由于int的表示范围更小,因此可能发生溢出。在不同数据类型之间转换时,往往隐藏着一些不容易察觉的错误,编程时要非常小心。

算术逻辑单元(ALU)

在计算机中,运算器承担了执行各种算术和逻辑运算的工作,运算器由算术逻辑单元(Arithmetic Logic Unit,ALU)、累加器、状态寄存器和通用寄存器组等组成。ALU的基本功能包括加、减、乘、除四则运算,与、或、非、异或等逻辑运算,以及移位、求补等操作。

计算机运行时,运算器的操作和操作种类由控制器决定。运算器处理的数据来自存储器;处理后的结果数据通常送回存储器,或暂存在运算器中。

串行加法器和并行加法器

ALU的核心部件是加法器,加法器是由全加器再配以其他必要的逻辑电路组成的,根据组成加法器的全加器个数是单个还是多个,加法器有串行和并行之分。

一位全加器

全加器(FA)是最基本的加法单元,有加数$Ai$、加数$B_i$与低位传来的进位$C{i-1}$共三个输入,有本位和$S_i$与向高位的进位$C_i$共两个输出。

全加器的逻辑表达式如下。

和表达式:$Si = A_i \oplus B_i \oplus C{i-1}(Ai, B_i, C{i-1}\text{中有奇数个}1\text{时},S_i = 1;\text{否则}S_i = 0)$

进位表达式:$C_i=A_iB_i+(A_i \oplus B_i)C_i$

一位全加器对应的逻辑结构如图2.15(a)所示,其逻辑符号如图2.15(b)所示。

串行加法器

在串行加法器中,只有一个全加器,数据逐位串行送入加法器中进行运算。若操作数长n位,则加法就要分n次进行,每次产生一位和,并且串行逐位地送回寄存器。进位触发器用来寄存进位信号,以便参与下一次运算。

串行加法器具有器件少、成本低的优点,但运算速度慢,多用于某些低速的专用运算器。

并行加法器

并行加法器由多个全加器组成,其位数与机器的字长相同,各位数据同时运算。并行加法器可同时对数据的各位相加,但存在一个加法的最长运算时间问题,原因是,虽然操作数的各位是同时提供的,但低位运算所产生的进位会影响高位的运算结果。例如,$11\cdots11$和$00 \cdots01$相加,最低位产生的进位将逐位影响至最高位,因此并行加法器的最长运算时间主要是由进位信号的传递时间决定的,而每个全加器本身的求和延迟只是次要因素。

因此,提高并行加法器速度的关键是尽量加快进位产生和传递的速度。并行加法器的进位产生和传递如下:

并行加法器中的每个全加器都有一个从低位送来的进位输入和一个传送给高位的进位输出。通常将传递进位信号的逻辑线路连接起来构成的进位网络称为进位链。

进位表达式为

式中,$G_i$时进位产生函数,$G_i=A_iB_i$;$P_i$时进位传递函数,$P_i= A_i \oplus B_i $。

当$Ai$与$B_i$都是1时,$C_i = 1$,即有进位信号产生,所以将$A_iB_i$称为进位产生函数或本地进位,并以$G_i$表示。$A_i \oplus B_i =1$且$C{i-1}=1$时,$Ci=1$。这种情况可视为$A_i \oplus B_i = 1$,第$i-1$位的进位信号$C{i-1}$可以通过本地向高位传送。因此,把$A_i \oplus B_i$称为进位传递函数(进位传递条件),并以$P_i$表示。

并行加法器的进位通常分为串行进位与并行进位。

(1)串行进位

把n个全加器串接起来,就可进行两个n位数的相加,这种加法器称为串行进位的并行加法器,如图2.16所示。串行进位又称行波进位,每级进位直接依赖于前一级的进位,即进位信号是逐级形成的。

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

(2)并行进位

并行进位又称先行进位、同时进位,其特点是各级进位信号同时形成。

采用并行进位的方案可以加快进位产生和提高传递的速度,即将各级低位产生的本级G和P信号依次同时送到高位各全加器的输入,以使它们同时形成进位信号,各进位信号表达式如下,可见它们可以同时形成进位信号:

上述各式中所有的进位输出仅由$Gi$、$P_i$,及最低进位输入$C_0$决定,而不依赖于其低位的进位输入$C{i-1}$,因此各级进位输出可以同时产生。

这种进位方式是快速的,与字长无关。但随着加法器位数的增加,$C_i$的逻辑表达式会变得越来越长,输入变量会越来越多,这会使电路结构变得很复杂,所以完全采用并行进位是不现实的。

分组并行进位方式,实际上通常采用分组并行进位方式。这种方式把n位全加器分为若干小组,小组内的各位之间实行并行快速进位,小组与小组之间可以采用串行进位方式,也可以采用并行快速进位方式,因此有以下两种情况。

${\textstyle\unicode{x2460}}$ 单级先行进位方式,又称组内并行、组间串行进位方式。以16位加法器为例,可分为4组,每组4位。第一小组组内的进位逻辑函数$C_1$、$C_2$、$C_3$、$C_4$的表达式与前述相同,$C_1$~$C_4$信号是同时产生的,实现上述进位逻辑函数的电路称为4位先行进位电路(CLA)。

利用4位CLA 电路及进位产生/传递电路和求和电路可以构成4位CLA加法器。用4个这样的CLA加法器构成的16位单级先行进位加法器如图2.17所示。

${\textstyle\unicode{x2461}}$ 多级先行进位方式,又称组内并行、组间并行进位方式。下面仍以16位字长的加法器为例,分析两级先行进位加法器的设计方法。第一小组的进位输出$C_4$可以写为

这种电路称为成组先行进位电路(BCLA)。利用这种4位的 BCLA电路及进位产生与传递电路和求和电路可以构成4位BCLA加法器。16位的两级先行进位加法器可由4个BCLA加法器和1个CLA电路构成,如图2.18所示。

这种方法可以扩展到多于两级的先行进位加法器,如用三级先行进位结构设计64位加法器这种加法器的优点是字长对加法时间影响甚小;缺点是造价较高。

算术逻辑单元的功能和结构

带标志加法器

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

在图2.19 中,溢出标志的逻辑表达式为$OF = Cn \oplus C{n-1}$符号标志就是和的符号,即 SF=$F{n-1}$﹔零标志$ZF=1$当且仅当$F=0$;进位/借位标志$CF =C{out} \oplus C{in}$,即当$C{in}$=0时,CF为进位$C{out}$,当$C{in}=1$时,CF为进位$C_{out}$取反。

值得注意的是,为了加快加法运算的速度,实际电路一定使用多级先行进位方式,图2.19(b)是为了说明如何从加法运算结果中获得标志信息,因而使用全加器简化了加法器电路。

算术逻辑单元(ALU)

ALU是一种功能较强的组合逻辑电路,它能进行多种算术运算和逻辑运算。由于加、减、乘、除运算最终都能归结为加法运算,因此 ALU的核心是带标志加法器,同时也能执行“与”“或”“非”等逻辑运算。ALU的基本结构如图2.20所示,其中A和B是两个n位操作数输入端,C是进位输入端,ALUop是操作控制端,用来决定ALU所执行的处理功能。例如,ALUop选择Add运算,ALU就执行加法运算,输出的结果就是A加B之和。ALUop 的位数决定了操作的种类。例如,当位数为3时,ALU最多只有8种操作。

图2.21给出了能够完成3种运算“与”、“或”和“加法”的一位ALU结构图。其中,一位加法用一个全加器实现,在ALUop的控制下,由一个多路选择器(MUX)选择输出3种操作结果之一。这里有3种操作,所以ALUop至少要有两位。

同时,ALU也可以实现左移或右移的移位操作。

补码加减运算部件

假设一个数的补码表示为Y,则这个数的负数的补码为$\bar{Y}$+1,因此,只要在原加法器的Y输入端加n个反向器以实现各位取反的功能,然后加一个2选1多路选择器,用一个控制端Sub来控制,以选择是将原码$Y$输入加法器还是将$\bar{Y}$输入加法器,并将控制端Sub同时作为低位进位送到加法器,如图2.22所示。该电路可实现补码加减运算。当控制端Sub 为 1时,做减法,实现$X+\bar{Y}+1=\lbrack x \rbrack\text{补}+\lbrack -y \rbrack\text{补}$;当控制端Sub为0时,做加法,实现$X+ Y=\lbrack x \rbrack\text{补}+\lbrack y \rbrack\text{补}$

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

可通过标志信息来区分带符号整数运算结果和无符号整数运算结果。

零标志ZF =1表示结果为0。不管是作为无符号数还是作为带符号整数来运算,ZF都有意义。

进/借位标志CF表示无符号数加/减运算时的进位/借位。加法时,CF= 1表示无符号数加法溢出,因此CF等于进位输出$C{out}$。减法时,CF =1表示有借位,即不够减故将进位输出$C{out}$取反来作为借位标志。综合可得CF=Sub$\oplus C_{out}$。对于带符号整数运算,CF没有意义。

溢出标志OF = 1表示带符号整数运算时结果发生溢出。对于无符号整数运算,OF没有意义。

注意:如对电路基础知识不太熟悉,可参阅电路相关教材的基础部分。对此章电路内容亦不必过分深究,目前统考对电路的要求并不高,且本节也不属于重点内容。

本章小结

在计算机中,为什么要采用二进制来表示数据?

从可行性来说,采用二进制,只有0和1两个状态,能够表示0、1两种状态的电子器件很多,如开关的接通和断开、晶体管的导通和截止、磁元件的正负剩磁、电位电平的高与低等,都可表示0、1两个数码。使用二进制,电子器件具有实现的可行性。

从运算的简易性来说,二进制数的运算法则少,运算简单,使计算机运算器的硬件结构大大简化(十进制的乘法九九口诀表有55条公式,而二进制乘法只有4条规则)。

从逻辑上来说,由于二进制0和1正好和逻辑代数的假(false)和真(true)相对应,有逻辑代数的理论基础,用二进制表示二值逻辑很自然。

计算机在字长足够的情况下能够精确地表示每个数吗?若不能,请举例。

计算机采用二进制来表示数据,在字长足够时,可以表示任何一个整数。而二进制表示小数时只能够用1/( $2^n$ )的和的任意组合表示,即使字长很长,也不可能精确表示出所有小数,只能无限逼近。例如0.1就无法用二进制精确地表示。

字长相同的情况下,浮点数和定点数的表示范围与精度有什么区别?

字长相同时,浮点数取字长的一部分作为阶码,所以表示范围比定点数要大,而取一部分作为阶码也就代表着尾数部位的有效位数减少,而定点数字长的全部位都用来表示数值本身,精度要比同字长的浮点数更大。

用移码表示浮点数的阶码有什么好处?

检验移码的特殊值(O和 max)时比较容易。阶码以移码编码时的特殊值如下。0:表示指数为负无穷大,相当于分数分母无穷大,整个数无穷接近0,在尾数也为0时可用来表示0;尾数不为零表示未正规化的数。max:表示指数正无穷大,若尾数为0,则表示浮点数超出表示范围(正负无穷大);尾数不为0,则表示浮点数运算错误。

常见问题和易混淆知识点

如何表示一个数值数据?计算机中的数值数据都是二进制数吗?

在计算机内部,数值数据的表示方法有以下两大类。

①直接用二进制数表示。分为无符号数和有符号数,有符号数又分为定点数表示和浮点数
表示。无符号数用来表示无符号整数(如地址等信息);定点数用来表示整数;浮点数用来表示实数。

${\textstyle\unicode{x2461}}$ 二进制编码的十进制数,一般都采用8421码(也称NBCD码)来表示,用来表示
整数。

所以,计算机中的数值数据虽然都用二进制来编码表示,但不全是二进制数,也有用十进制数表示的。后面一章有关指令类型的内容中,就有对应的二进制加法指令和十进制加法指令。

在高级语言编程中所定义的unsigned/short/int/long/float/double型数据是怎么表示的?什么称为无符号整数的“溢出”?

unsigned 型数据就是无符号整数,不考虑符号位。直接用全部二进制位对数值进行编码得到的就是无符号数,一般都用补码表示。

int型数据就是定点整数,一般用补码表示。int型数据的位数与运行平台和编译器有关,一般是32位或16位。例如,真值是-12的int型整数,在机器内存储的机器数(假定用32位寄存器寄存)是1111 1111 1111 1111 1111 1111 1111 0100。

long 型数据和 short型数据也都是定点整数,只是位数不同,分别是长整型和短整型数,通常用补码表示。

float型数据是用来表示实数的浮点数。现代计算机用IEEE 754标准表示浮点数,其中 32位单精度浮点数就是 float型,64位双精度浮点数就是double型。

需要注意的是,C语言中的int型和unsigned型变量的存储方式没有区别,都按照补码的形式存储,在不溢出范围内的加减法运算也是相同的,只是int型变量的最高位代表符号位,而unsigned型中的最高位表示数值位,两者在C语言中的区别体现在输出时到底是采用%d还是采用%u。

  • $\blacktriangleright$(什么叫都是用补码来存储,为什么unsigned 也是用补码来存储)

对于无符号定点整数来说,若寄存器位数不够,则计算机运算过程中一般保留低n位,舍弃高位。这样,会产生以下两种结果。

①保留的低n位数不能正确表示运算结果。在这种情况下,意味着运算的结果超出了计算
机所能表达的范围,有效数值进到了第n+1位,称此时发生了“溢出”现象。

${\textstyle\unicode{x2461}}$ 保留的低n位数能正确表达计算结果,即高位的舍去并不影响其运算结果。

如何判断一个浮点数是否是规格化数?

为了使浮点数能尽量多地表示有效位数,一般要求运算结果用规格化数形式表示。规格化浮点数的尾数小数点后的第一位一定是个非零数。因此,对于原码编码的尾数来说,只要看尾数的第一位是否为1就行;对于补码表示的尾数,只要看符号位和尾数最高位是否相反。需要注意的是,IEEE 754标准的浮点数尾数是用原码编码的。

对于位数相同的定点数和浮点数,可表示的浮点数个数比定点数个数多吗?

不是,可表示的数据个数取决于编码所采用的位数。编码位数一定,编码出来的数据个数就是一定的。n位编码只能表示$2^n$个数,所以对于相同位数的定点数和浮点数来说,可表示的数据个数应该一样多(有时可能由于一个值有两个或多个编码对应,编码个数会有少量差异)。

浮点数如何进行舍入?

舍入方法选择的原则是:①尽量使误差范围对称,使得平均误差为0,即有舍有入,以防误差积累。②方法要简单,以加快速度。

IEEE754有4种舍入方式。

①就近舍入:舍入为最近可表示的数,若结果值正好落在两个可表示数的中间,则一般选
择舍入结果为偶数。

${\textstyle\unicode{x2461}}$ 正向舍入:朝 + $\infty$ 方向舍入,即取右边的那个数。

③负向舍入:朝 -$\infty$ 方向舍入,即取左边的那个数。

④截去:朝0方向舍入,即取绝对值较小的那个数。

现代计算机中是否要考虑原码加减运算?如何实现?

因为现代计算机中浮点数采用IEEE754标准,所以在进行两个浮点数的加减运算时,必须考虑原码的加减运算,因为IEEE 754规定浮点数的尾数都用原码表示。

原码的加减运算可以有以下两种实现方式:

1)转换为补码后,用补码加减法实现,结果再转换为原码。

2)直接用原码进行加减运算,符号和数值部分分开进行(具体过程见原码加减运算部分)。

长度为n+1的定点数,按照不同的编码方式,表示的数值范围是多少?

各编码方式的数值范围找不到图片(Image not found)

设阶码和尾数均用补码表示,阶码部分共K+1位(含1位阶符),尾数部分共n +1位(含1位数符),则这样的浮点数的表示范围是多少?

表2.9浮点数的表示范围找不到图片(Image not found)

c语言相关

基本数据类型所占的字节数

类型字节数
char1
short2
int4
long4
float4
double8

$2^8$ = 256

$2^{16}$ = 65536

$2^{32}$ = 4294967296

$2^{64}$ = 18446744073709551616

溢出那些事

unsigned int 之间做减法产生负数会怎么样

边界对齐

边界对齐:参考文献

补码那些事

使用补码表示时(整数和定点小数都有如下结论),若符号位相同,则数值位越大码值越大。

移位那些事

一定要记住符号位不动,变动的是数值位

符号扩展那些事

需要保证数值不变

关于门电路相关的知识

见习题