【计组】带你彻底搞清楚计算机中的数据表示(一)

0. 前言

        计算机组成原理中的数据表示相关概念非常多,诸如原码、补码、反码、移码、浮点数、IEEE754、BCD码、ASCII码、奇偶校验码、循环冗余校验码、海明码、补码溢出判断……而且大部分教材在这一块的内容上都不说人话,想必有很多同学会觉得难记、难理解,所以本系列文章将尽量带着大家尝试从理解设计者设计初衷的角度出发,去逐一掌握这些纷乱繁杂的数据表示。

        在开始学习之前,大家需要先明确一点,数据表示对计算机组成是非常重要的,一个更简便的数据编码方式,进行运算时底层的硬件电路可能就会更简单,不但更易扩展,效率也更高,所以设计者在设计数据编码方式时,肯定是怎么方便电路设计怎么来,怎么效率更高怎么来。

目录

0. 前言

1. 数据编码

1.1 数值数据编码 

(1)补码概念的引入

(2)补码的定义与性质

(3)反码的表示

(4)移码概念的引入

(5) 移码的表示与性质

总结和预告


1. 数据编码

        

1.1 数值数据编码 

        在计算机中,我们把用软件实现的数据类型称为数据结构,用硬件实现的数据类型称为数据表示,在今天我们要研究的就是数据表示。

        首先,谈到数据表示,最简单的肯定就是原码了,对于原码而言:

  1. 我们认为+0和-0是一样的
  2. 原码所能表示的范围在负方向和正方向上是对称的,-127~+127
  3. 原码进行乘除法比较方便,但涉及加减法就比较麻烦

        计算机中加减运算的出现是极为频繁的,如果能够提高加减运算的效率,无疑能大大提升整体效率,所以我们希望有这样一种数据表示,能够把减法运算也转变为加法运算,这样底层的硬件实现就简便了,效率也提升了,计算机科学家们呢,就设计了一种数据表示:补码。

(1)补码概念的引入

        要想把补码讲明白,在这里就必须先引入两个概念:无模运算和有模运算。大家可能会觉得陌生,但其实我们生活中就有一个经典的有模运算的例子:时钟时间的计算

        

         当顺时针拨动时针时,时间增加;逆时针拨动时针时,时间减少。如图所示,现在的时间是8点整,如果我们把时针逆时针拨动三大格,则时间变为5点整,数学表示就是8 - 3 = 5,由于时钟时针的范围是0~11,当时针指向12时,我们认为又从0开始了,也就是说,钟表的模就是12,换句话说,我们加上12的整数倍时,不会对时间的数值产生影响,所以我们也可以这样做:

        我们将时针顺时针拨动9大格,可以发现:时针指向的依然是5,这就说明,对有模运算体系而言,我们可以把加法转化为减法,这里的9,我们就把它称为-3的补数。

结论:

  1. 假定M为模,若数a、b满足a+b=M,则成a、b互为补数
  2. 在有模运算中,减去一个补数,等于加上这个数对模的补数

        那这又和补码有什么关系呢?由于计算机中的字长是有限的,所以其运算就是天然的有模运算,也就是说可以非常方便的用补数来运算:

四位二进制的模为16,则在有模运算中,12-3=12-3+16=12+13

        但是这样好像有些奇怪,这个13是怎么来的?不还是需要16-3求得13吗,这不还是涉及了减法?感觉还是没有避开减法运算呀?

        事实上,由于二进制的特殊性质,我们可以通过非常简单的做法得到一个负数的补码。

有模运算:

(2)补码的定义与性质

补码的定义

        设模为M,则一个数X的补码的一般定义为:|X|补 = M + X

描述:

  1. 若X>0,则模M作为超出部分被舍去,|X|补=X,因而正数的补码就是其本身
  2. 若X<0,则|X|补 就是|X|以M为模的补数

        来看看教材上关于一个数的补码的定义,显然只要我们能够写出定点小数(纯小数)和定点整数(纯整数)的补码,后面我们就能够用浮点数去表示大于1的非整数了

补码的定义:

        可能有同学会感到困惑,为什么求定点小数的补码时,加的模是2呢?

        大家需要明确一个点,定点小数的小数点左侧一位是符号位,0表示这个数是正小数,1表示这个数是负小数,所以负小数的模应该就是2:

        除此之外,还有一个值得一提的点是,一般来说纯小数是不能表示1的,然而0.00……0表示+0,原本1.00……0是表示-0,但是规定补码只能由一个0,所以1.00……0表示-1而不是-0,也就是说,纯小数的补码在负方向上能多表示一个数-1。

        类似的,100……0也不能表示-0,是能表示-2^(n-1),也就是说,纯整数在负方向上也能多表示一个数。

综上,补码相对于原码,可以在负方向绝对值最大的位置表示一个数。

       

补码的性质

        首先我们要谈的是,补码与原码间的相互转换:此处我们以定点小数为例子,而对于定点整数也是类似的。根据定义,正数的补码与原码是相同的,所以我们只讨论负数的补码与原码之间的相互转换。

        所以补码和原码可以通过数值按位取反,末尾加一的方式进行相互转换。

        想必大家会这样想:虽然说按位取反,末尾加一确实避免了减法,但还是觉得好麻烦啊!所以我们还要再接着探寻一种更加简便的求负数补码的方式!

        我们可以发现一个规律,把一个二进制数按位取反,再加一之后,从右往左数,第一个1的右侧的0全部不变,这个1的左侧全部取反了;也就是说,我们其实没有必要去按位取反,再加一就可以得到一模一样的结果。

        结论:数值部分自低位向高位搜索,第一个1及其以右的各位0保持不变,以左的各高位按位取反

        希望大家能够记住这个结论,有了它,我们就可以非常简便地进行补码与原码间地的相互转换了,而且还不容易出错,熟练使用这个结论,解题速度将大大加快。

        

        接下来我们来讨论一下补码的符号位扩展,符号位扩展可以分为定点小数的符号位扩展、定点整数的符号位扩展,先来看看定点小数的符号位扩展:

        定点小数的符号位扩展并没有什么值得提的地方,只需要在末尾补0就行了。再来看看定点整数的符号位扩展:

综上所述,我们得出一个结论:、

将整数补码的模扩大2^n倍,只需将 [X]补 的符号位向左复制n位即可

举个实例:

        学习了补码的符号位扩展后,我们接下来要掌握的是:补码的算数右移(实际上就是除2运算),为了节约篇幅,我们以定点小数为例进行证明,定点整数情况下的证明是完全类似的。

        既然有算数右移,相应的一定就有算数左移(乘2运算),而我们知道,计算机中补码的运算是有模运算,乘2的操作可能会溢出,所以要进行处理

结论:已知[X]补求[2X]补将[X]补的各位左移一位,末尾补0即可      

举个算数左移导致溢出的例子:

        可以发现,X2这个正数,在乘2后反而变成了负数,绝对是发生了溢出。现在我们来分析一下现象,为了表示有符号数,我们设定了最高位为表示正负的符号位,而有模运算的算数左移意味着舍弃掉最高位,也就是说,左移一位就舍掉了一位的符号数。所以我们可以看到X1的最高位与次高位相同,可以视为是符号位的扩展,所以即使舍掉一个符号位也不会溢出,X2则溢出了。

        因此我们可以通过双符号位来实现判别溢出,这在计算机实现中被称为变形补码

        总结一下补码的性质:

  1. 用补码表示的数,最高位为0,则该数是正数;若为1,则该数是负数
  2. 0的补码是唯一的
  3. 补码能比原码在负方向上多表示一个数
  4. 负数的补码值大于正数的补码值
  5. 补码与真值、原码间的相互转换:

        对于真值大于0的情况,三者完全相同;当真值X<0时,根据定义可得:

        6. 补码符号位的扩展:

                

        7.  补码的算术右移:

  

        8. 补码的算数左移:

               

(3)反码的表示

                由于计算机中反码用的不多,而且一般来说,教材上将反码都是为了引入补码,但是我们用另一种方式同样很好的引入了补码,所以在这里我就不详细地讲反码了。

        正数的反码和原码相同,负数的反码可通过原码按位取反得到。

(4)移码概念的引入

        为了让大家更好的理解移码是什么,以及为什么发明移码,我不想一上来就抛出一大堆概念,而是先通过一个场景来在引入移码的同时,让大家理解浮点数。

        在上文我们指出了,计算机中的整数是以补码的形式储存的,那非整数呢?大家应该都知道科学计数法吧,对于一个非整数,我们为了方便储存,使用科学计数法的方式将其进行规格化,则这个数的编码就应该能够表示:符号位、尾数、阶码

                

        如上图所示,我们规定小数点左侧的一位表示符号位,小数点右侧的数称为尾数(为了方便后续进行加减乘除运算,尾数就用原码来存),而阶码指的自然就是2的阶数了,那么阶码该用什么表示呢?

        原码和补码都有符号位,并且正数的符号位为0,负数的符号位为1,直观感受肯定是1比较大,所以如果用原码或者补码来保存,底层的硬件肯定还要加上判断这个符号位的电路,总归还是太麻烦了。而反码就更不用说了,所以我们现在需要的是一个,能和真值成线性正比的编码方式,所以,移码就被设计出来了。

        顺便一提,由于在浮点数进行规格化之后,我们比较的顺序是先看符号位,再看阶码,再看尾数,所以为了底层实现方便,我们将编码的顺序稍微改一下:

        接下来我们来尝试设计一个简单的,3位编码的移码,既然要让移码和真值成线性正比,我们就使用偏移量的方式实现,则此时需要选择一个基准与真值0对应,依然是为了后续运算的简便,我们选择比较中间的数与0对应

(5) 移码的表示与性质

        前面我们通过设计3位移码来学习了移码是如何用偏移量实现与真值成线性正比的,但大家也都清楚,计算机中的编码往往是8位的,既然我们也学到移码了,就在此顺便总结一下目前为止学到的几种编码对应的真值范围:

        根据我们所设计的3位移码,总结一下移码的性质:

        关于移码,我们最后再研究一下它和补码间的关系:

        因为2^(n-1)实际上就是一个n位为1,其余位为0的二进制数,根据有模运算的性质 ,加上这样一个数,其实就是对最高位进行取反,而补码和移码的最高位正好又表示符号位,所以移码和补码的关系可以这样用语言表述:数值上,移码和补码是符号位相反的关系。

总结和预告

        本篇文章和大家分享了我在数据表示中的数值数据编码部分内容的学习总结,我们通过生活中的例子,较好地理解了补码这个在计算机数据表示中比较重要的编码方式,并通过浮点数的表示来引入了移码。不仅如此,我们还学习了一些关于补码与真值、原码、移码间快速转换的结论,希望大家下来可以动手多练习几道题,这样才能更好地掌握知识。

        预告一下,下一篇文章将为大家讲解浮点数与IEEE754标准,好了,大家下一篇文章见!

  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值