5.1.4_带符号整数的表示和运算_原反补

简介: 计算机组成原理之带符号整数的表示和运算_原反补

[toc]

一、带符号整数

这个小结中我们要学习带符号整数在计算机内部如何表示,以及如何进行加减运算。

我们会介绍原、反、补三种编码方式。

首先来看一下什么是带符号整数

很简单,在数学里边的整数的概念就是,计算机里边的带符号整数,也就是数学里边的Z。z,什么- 2、- 1、0、1、2、3、4 这些都是带符号的整数。
image.png

在 c 语言当中,我们最常用的应该就是带符号整数。比如short, Int 这些都是带符号的整数,只不过 short 和 Int 它们的长度各不相同而已。

它们的比特位数不同,可以表示的数值范围自然也会有一些区别。

这个小结当中我们要探讨的就是这种带符号的整数在计算机硬件内部应该如何去表示,以及我们要对带符号整数进行加法、减法运算,用计算机硬件实现的基本原理是什么?
image.png

这是小结的主要内容。

二、原码反码补码

(1)介绍

大家可能会奇怪,为什么带符号整数有所谓的原码、补码、反码这样的三个东西?
image.png

其实这个不难理解,我们要在计算机内部表示一个带符号整数的值。同一个含义,我们可以用不同的编码方式,也就是可以用原码来表示,也可以用补码来表示,也可以用反码来表示。

它们的呈现的形式会各不相同,但是背后的含义都是一样的。你可以用不同的编码方式去表达,不同的编码方式会有各自的优点和缺点。


上一小节我们说过,计算机的机器字长限制了同时可以进行多少个比特位的运算,也限制了它的通用寄存器的长度可以存多少位的信息。
image.png

因此,由于硬件的限制,也意味着一台计算机它可以表示的带符号整数,它的比特位位数是有上限的,不能超过它的机器字长。

所以这个小节我们会以机器字长为8比特的计算机为例,介绍带符号整数的表示和运算。

(2)原码

1.介绍

接下来我们先介绍最简单的原码表示法。

我们把更低的这些比特位看作是数值的信息,把这些比特位称为数值位

用最高的比特位表示正负号, 0 表示正, 1 表示负。如下:
image.png

所以一个有符号的整数就可以表示为1个符号位,加上 n - 1 位的数值位。

因此正的 19 这个值我们转换成二进制,应该是这样表示。

把正号(+)这个符号转变为符号位的 0 ,数值部分填到对应的位置就可以了。如下:
image.png

负 19 和正 19 相比,只是差了一个符号位。所以数值位的这些信息和正 19 是相同的,只有符号位是1,表示它是一个负数。如下:
image.png

这就是带符号整数的原码表示法。

2.要点

我们来总结一下。

首先用 0 和 1 表示正和负,剩下的 n - 1 个比特,全部都是数值位的信息,每一个数值位会有一个对应的位权。

假设我们是用 n + 1 个比特来表示一个有符号数的原码,总共 n + 1 个比特。

我们需要留出最高的比特来表示符号,数值位就只剩下 n 个比特,n 个比特可以表示的数值范围应该是多少?我们上一小节提到过,应该是 0~(2^n)-1
image.png

所以数值位可以表示这样的一个范围,再加上符号位的正号和负号。

因此整个 n + 1 比特的原码可以表示的合法范围应该是,最小可以表示负的 2的 n次方减1,最大可以表示 2 的 n 次方减 1 。
image.png

值得注意的是,真值 0 用以原码有两种表示形式,一个是正0,一个是负0。

正 0 和负 0 的区别就是符号位是0还是1,但是它们俩代表的真值是相同的。

3.特性及书写

对于原码,大家需要注意这样的两个特性,一个是它的表示范围,另一个是真值 0 的表示形式有两种。

在我们试卷里边,一种常见的表示形式是这样的,它会告诉你一个 x 的真值,用十进制表示。 x 所对应的原码它会用这样的方式来书写。就是打一个中括号,加一个这样的角标。如下:
image.png

你答题的时候可以这么书写。

符号位的后面用一个小逗号隔开,后面再跟上数值位的信息,就是这样1,0010011。(机器字长8bit)

当然,如果题目没有告诉你说机器字长到底是多少个比特,你可以这么写。一个符号位和后面的几个数值位,前边无效的这两个0,这两个数值可以把它去掉,就是这样1,10011

这是在题目没有指明机器字长的情况下,可以这么来写。

4.运算

现在我们已经知道了有符号的整数用原码的形式如何表示。

接下来我们来看一下基于原码表示的有符号整数要如何实现加减运算

来看一个最简单的例子。

加法运算怎么实现?

假设我们有A、 B 二个数,分别是正 19 和负19,它们对应的8比特原码我已经在这个地方写出了。

如果采用上一小节介绍的那种加法实现的方式,从最低位开始,逐位相加,最后的结果应该是这样的,大家可以自己手算一下。
image.png

显然,这是一个错误的结果。 A 和 B 相加,正确的结果应该是0。

但是我们用刚才的方式逐位相加,最后的结果错了。

问题出在哪?问题出在这个地方。
image.png

就是最高的这一个比特,它表示的并不是数值的信息,而是符号的信息

所以如果我们逐位相加,从低位加到高位,这个符号位我们同样按加法的这种规则来处理,就会导致最后的结果出现错误。

所以这就是原码表示法它的一个缺点。

符号位是不能参与加减运算的。如果要实现原码的加减运算,我们需要设计一个非常复杂的硬件电路才可以处理。

电路越复杂就意味着越费钱、越贵,我们的计算机造价就会越高,谁会跟钱过不去呢,对吧?
image.png

所以,为了节约计算机硬件电路的设计成本,聪明的科学家们发明了补码表示法。

如果用补码来表示有符号整数的话,那么符号位也可以用同样的方式来参与运算

一会我们会用具体的例子带大家感受一下。


(3)反码和补码

1.运算

现在我们把注意力放在这个地方。

既然用原码来实现加减运算不方便,我们就可以想办法把原码转换成与之等价的补码表示

计算机是怎么进行原码到补码的转换的?

直接给一个图。
image.png

在原码转换成补码的过程中,还需要经历一个中间步骤,就是需要先转变成反码,然后再用反码转变成补码

用原码表示有符号整数,更便于我们理解。最高位是符号位,其余的都是数值信息。

对于计算机来说,如果把原码转变成补码,会更方便硬件电路去实现加减运算。

来看一下计算机怎么把原码转换为与之等价的补码。

<1> 首先,如果是一个正数,它的原码、反码和补码表示都是一样的,保持不变就可以好。

所以对于正19,它的原码是这样子,反码、补码也一模一样。
image.png

而对于正的50,由于它是一个正数,所以原码、反码和补码的表示也都一样。
image.png

所以正数很好处理,三种码都是一样的。

<2> 接下来看负数原码,首先它要转换成反码

转换的方法是符号位保持不变,而数值位按位取反

对于-19 数来说,符号位保持不变,数值位按位取反,应该是11101100,这就是- 19 的反码表示。
image.png

得到了反码之后,我们再在反码的末位加 1 就可以得到补码

末位加 1 应该等于11101101,这就是- 19 的补码表示。
image.png

同样的,对于三种码的转换过程,大家不需要去纠结为什么要这么做,你只需要记住计算机它是怎么做的就可以了。


我们再用一个负数来练手,比如-100

首先写出它的原码,应该是1,1100100这个样子,大家可以自己来试一下。

原码首先转变为反码,符号位保持不变,数值位按位取反,就是1,0011011。

然后在反码的基础上,末位再加一个1,就可以得到补码1,0011100。
image.png

<3> 注意

值得注意这样的几个地方。

首先,不管是原码、反码还是补码,它们的最高位都反映了符号,也就是正负的信息。最高位为0,表示这是一个正数,最高位为1,表示这是一个负数。

第二点注意,我给大家总结的图里边,左边这个箭头是双向的,右边这个箭头是单向的。
image.png

原码转变成反码和反码转变回原码,它们的做法都是一样的,都是符号位保持不变,数值位按位取法。

但是反码转变为补码以及补码转变回反码这两个过程是不一样的。反码转补码,只需要在末位加 1 就可以。 补码转回反码,照理来说应该是在末位减1,这么操作才可以从补码转变回反码。

在考试的时候,如果让你把补码转变成反码,不建议用末位减 1 这种方式来计算,因为二进制的减法,你用手算会比较麻烦。

考试的时候可以怎么做?你可以先把补码转变成原码,再用原码去求反码,这样转变的速度反而会更快。

2.手算

原码-->补码

接下来教大家一种原码和补码之间快速转换的一个手算的方法,这个方法在考试的时候会非常的快。
image.png

来看一下怎么做。

<1> 首先对于正数来说,原码和补码它们是一样的,保持不变就可以不用转换。
image.png

<2> 对于负数来说,也如果你看到这个码,它第一位最高位是 1, 是一个负数,它的原码和补码之间就会有区别。

如何快速地转换?看这一句话,从右往左找到第一个1,在 1 的左边,所有的数值位都按未取反。
image.png

来看一下怎么做。

  • 比如-19

我们现在尝试着把-19 的原码直接转换为-19 的补码。

①从右往左找到第一个1,显然这是第一个 1 。如下图:
image.png

②在这个 1 的左边,所有的数值位全部按位取反,也就是这个范围内(如下图)的数值位,这些数值位全部按位取反。
image.png

③然后符号位保持不变,还有刚才找到的 1 以及它右边的部分保持不变。
image.png

你看一下是不是和它的补码一样的。
image.png

所以用这种方法可以非常快速地把原码转换成补码。

  • 比如-100

再用-100 来试一下,把- 100 的原码转变成与之对应的补码。
image.png

首先如果给你的是二进制,你得先看它的符号位,符号位是1,表示它是一个负数,负数的原码转补码,我们用刚才的那种方法。

从右往左找到第一个1,看一下第一个 1 ,这个1 和右边的部分保持不变(100)。

这个1 左边部分的所有数值位(1100)全部按位取反得到0011。

符号位也保持不变。

所以用这种方法可以快速转换得到的结果,你看一下是不是也是一样的。
image.png

所以在大家做题的时候,不需要把原码转反码,再从反码转补码。这种做法是计算机处理的方式,我们用手算这么来处理会快得多,而且不容易出错。


来简单解释一下为什么这么做,可以直接快速的得到补码。

按照我们之前计算的方法,我们知道原码要转补码,首先用计算的方法是把所有的数值位按位取反,然后末位要加一个1。

如果这个反码的最后这一位是 1 或者这个1前边还有别的1,那么我们在末位加上 1 之后,就会导致不断地向前进位。
image.png

最后面的这两个 1 相加等于0,要往前进 1 。如下:
image.png

前面这一位 1 再加刚才进位进上来的 1 又变成了0,再往更高位进1。如下:
image.png

直到什么时候进位会停止呢?

直到前边这一位,它是 0,0 加刚才进上来的1,这一位就是1,就不会再往更高位进位了。如下:
image.png

这就意味着从这个位置再往前的这些部分,应该是和反码保持一致。如下:
image.png

而刚才画的这条线,右边的这些部分,由于不断地往前进位, 1 变 0、0 变1,所以导致了后面的这些部分和以前的原码是保持一致的。
image.png

所以为什么我们在原码的基础上,从右往左找到了第一个1,这个 1 的右边保持不变,而左边全部取反。就是这个原因,大家可以自己琢磨一下。

这就是原码快速转补码的方法。


补码-->原码

如果要把补码逆向的转换回原码,怎么做?做法也是一样的。

同样是在补码的基础上,从右往左找到第一个1,以 这个1 作为分界线,所有的数值位按位取反就可以回到原码
image.png

所以负数的原码转补码和补码转原码,它的手算的方法都是一样的。

对于计算机来说,如果要把补码转换回原码,它是怎么做的?

它的做法和原码转补码也是一样的。首先从补码出发,符号位不变,数值位取反,再在末尾加1。
image.png

所以补码转回原码对于计算机而言,它做的处理也是一样的。

符号位保持不变,数值位取反,末尾加1。

3.补码的运算

现在我们已经知道了怎么把原码快速的转换成补码。还记得刚开始我们为什么要引入补码吗?因为计算机对原码进行加减操作不方便。所以我们引入补码,是为了让加减运算操作起来更方便。

接下来用几个例子来感受一下用补码进行加减运算是怎么做的。

加法运算

首先来看补码的加法运算

<1>案例一

A 是正19, B 是负19,计算A+B等于多少。
image.png

现在大家应该有能力自己快速地写出这两个真值对应的补码形式了,你先写出它的原码,再用原码转换成补码就行了。
转换的结果大家可以自己来试一下。

接下来我们要对A、 B 这两个数进行加法运算。加的规则和之前我们介绍的一样,从最低位开始依次相加,并且要考虑往更高位进行进位

来看一下,最低这位 1 + 1 = 0,往更高位进1;前边这位 1 + 0,再加 1 等于 0,往更高位进1。
image.png

以此类推。

最高位是符号位,但这个符号位用相同的逻辑来处理就可以了,所以它的处理方式依然是 0 + 1再加1,这一位等于0,往更高位进1。如下:
image.png

最终我们只保留更低的 8 个比特。

最前边这一位我们给他丢弃,得到的结果全部都是 0,0这个补码它所对应的真值就是 0 。
image.png

所以在计算机内部,如果我们能够先把原码转换成补码再进行加法操作,就很方便。

符号位不需要特殊的处理,和其他的数值位一样就可以了

值得注意的是,补码的各个数值位,我们不能像原码那样把它解读为位权的信息。

对于正数的补码,你要把它解释为位权可以。比如第二位,它的位权就是 2 的1次方。

但是对于负数的补码来说,就不能把这些 0101 解释成位权了。
image.png

比如这一位它是1(上图),但是你不能把它解读为是 2 的二次方。

所以你会看到,如果用补码来表示数值,对于这种负数来说,我们不可能直接就看出它对应的真值是多少。

如果要确定它的真值,只能先把补码转换成人类能够看得懂的原码,再去解读它的真值是多少,只能用这种方式来处理。

补码对人类不友好,但是对计算机硬件很友好。计算机硬件非常擅长做一些重复性的事情,如果所有的这些比特位都是用同样的规则去处理,这是计算机非常喜欢的事情。


<2>案例二

通过第一个例子,我们感受到了补码进行加法运算它的一个魅力。

接下来我们再用第二个例子来感受一下。

A 是负19, B 是负19,计算A+B等于多少。

我们先写出-19 的补码,然后逐位相加,大家可以自己练练手,我们就不再重复了。

从最低位开始,逐位相加,逢 2 进1,从最低位逐个位往前处理,符号位也用相同的方式处理。

所有的这些比特都按位相加了之后,得到的结果是这个样子。
image.png

同样会有一位是多出来的,我们把多的这一位给抛弃掉,不要只保留更低的 8 个比特就行。

保留下来的这 8 个比特,我们按照补码的规则对它进行解读。

由于是复数,而复数的补码我们人类是看不懂的,只能把它转换为原码。转换的方法就是从右往左找到第一个1,在 1 的左边画一条分界线,在分界线之内的所有的这些比特位,你全部把它按位取反。
image.png

算出原码10100110。看一下这个原码它对应的真值是多少。每一个位的位权是1、2、4、8、16、32。所以原码它对应的值应该是32加上 4 加上2,刚好就等于 38 ;再结合符号位的信息,符号位是1,所以它表示的是-38
image.png

OK,所以我们看到这两个负数补码相加之后的结果,我们同样按补码去进行解读,这个运算是正确的。

现在补码的加法运算我们了解的差不多了,核心就是补码的符号位也会参与加法运算,符号位不需要单独地处理。这就是为什么补码的加法比原码的加法更方便计算机去实现的原因。

减法运算

现在聊完补码的加法,我们再来看补码的减法运算怎么实现。

假设有A、 B 这样的两个数,分别都是用补码表示,它们之间的减法运算应该怎么来实现?
image.png

这个地方又要考虑到加法电路和减法电路的设计成本问题。加法电路制造起来很便宜,减法电路制造起来很贵。所以我们最好是想办法把减法转变为等价的加法运算,可以省钱
image.png

OK,怎么把减法变成加法?

很简单, A 减 B 是不是可以等价于 A 加上负B?所以如果用补码来表示 A 和 B 的值,我们要计算 A 减 B 是不是就可以等价地转换为 A 的补码,加上负B 的补码,这样我们就可以把减法转换成等价的加法。
image.png

现在我们面临的问题就是,怎么通过 B 的补码来求出负B 的补码。

求法大家肯定会有似曾相识的感觉。如果给你一个数的补码,你要求这个数的负数所对应的补码,你只需要把它全部位所有的位都按位取反,末尾加 1 就可以。是不是有种似曾相识的感觉。
image.png

用 19 和- 19 来验证一下,试一试这种方法好不好使。

先来看19,要把 19 的补码转换为- 19 的补码,我们首先要做的是全部位按位取反,注意是全部的位包含符号位。

全部取反,然后末尾加1,得到这样的一个结果,可以看到,和-19 的补码是一样的。
image.png

现在再试一下逆向的转换,我们从- 19 的补码转换成 19 的补码,做法还是一样的。全部位按位取反,包括符号位在内都要按位取反,然后末位加1。

得到这样的一个东西,你看和正的 19 的补码是不是一致。
image.png

这是考试的重点,就是给你一个数的补码,让你求这个数的负值的补码表示。

很简单,全部位按位取反,然后末位加1。


讲到这儿,一些比较机灵的同学可能想到,有没有更快的手算方法

有,和之前我们介绍原码转补码的方法很像。

<1> 比如把19的补码转为-19的补码

同样的,从右往左找到第一个1,画一条线。 这个1 左边所有的部分,包括符号位在内所有的部分,全部都取反, 1 变 0、0 变1。刚才画的这条线右边的部分保持不变。
image.png
你看是不是就快速地进行了转换。
image.png

<2> 把- 19 的补码转换成 19 的补码也一样的。

从右往左找到第一个1,这个1左边画一条分界线,这条分界线左边所有的部分包含符号位在内,全部都按位取反。
image.png

刚才画的这条线右边的部分保持不变,就得到:
image.png

你看这样是不是就快速地得到了它的负值的补码表示。

注意不要晕,刚才我们从右往左找到第1,画了一条分界线,它左边全部的位都按位取反了,跟之前原码转补码是有一些区别的。

原码转补码的过程,我们找到第一个 1 之后,只是把这个 1 左边所有的数值位进行了取反,符号位是保持不变的。
image.png

注意这个区别,别搞错了。


回到刚才的主线。

补码的减法运算怎么实现 A 减B,这个运算可以把它转变为A加上负B 的补码,如何用 B 的补码得到负B 的补码呢?

刚才我们已经给出了完整的方法,接下来是不是就可以把减法转变为等价的加法操作了?

讲到这儿,不知道大家还记不记得我们讲无符号整数的减法运算的时候,采用的也是相同的处理方法。

对两个无符号整数进行减法,我们会把减法转变为等价的加法。处理的方式是,把减数全部位按位取反,然后末位加1,这样就可以用加法来实现减法。如下:
image.png

你看对减数的处理方法是不是一模一样,这意味着什么?我们只需要用同一套电路就可以实现无符号整数的减法以及有符号整数的补码减法,这就意味着电路更简单,制造起来也更便宜。
image.png

所以回到补码的减法运算,我们用一个实际的例子来感受一下。

A 是正19, B 是负19,它们的补码表示这儿已经给出了。
image.png

我们要计算 A 减B,可以把减法等价为 A 加上负B。

因此我们需要求出负B 的补码表示。

求的方法刚才已经说过了,把减数 B 的补码全部的位都按位取反,然后末位加1,这是计算机的处理方式,这样就可以得到负B 的补码。

接下来我们只需要用 A 加上负B 就可以了。
image.png

加法运算我们已经说过很多次了,从最低位开始逐位相加,需要考虑往更高位的进位,大家可以自己来试一下最后相加的结果。
符号位为0,表示它是一个正数。而正数的补码和原码是一样的,所以你可以直接把尾数进行解读。 刚好是正的38。
image.png

因此我们用刚才的这种处理方式,把减法转变为等价的加法,最后得到的结果是正确的。

所以无论是对带符号整数进行减法运算,还是对无符号数进行减法运算,在计算机的视角来看,它的处理都是一样的。

都是把减数全部位按位取反,末位加 1 ,让减法变成等价的加法,接下来再从最低位开始逐位相加,并且要考虑往更高位进位,用这样的方式来处理就可以了。
image.png

这样我们用同一套电路来处理所有的加法减法,省钱,电路的制造成本就更低。
image.png

三、总结回顾

image.png

这一小节,我们介绍了计算机内部带符号整数怎么用原码、反码和补码表示,大家需要非常熟悉这几种码之间的相互转换。
同时我们也介绍了带符号数,以及在计算机硬件看来,加法减法是怎么实现的。

值得一提的是,计算机内部所有的这些带符号数,它要进行加法减法之前,都需要先转换成补码的形式。

在你写 c 语言程序的时候,像 Int 型的带符号整数,它本身就是用补码来表示的。

现在你应该知道了为什么要用补码来表示你的带符号整数,因为对它进行加减运算的时候会更方便。

好的,以上就是这小节的全部内容。

相关文章
|
14天前
两个整数相加
【10月更文挑战第12天】两个整数相加
16 5
|
人工智能 测试技术
MoonLight的运算问题
MoonLight的运算问题
93 0
|
6月前
|
C++
c++数字运算
c++数字运算
30 0
wustojc2002整数四则运算
wustojc2002整数四则运算
62 0
|
存储 C++
大整数运算(高精度运算)C/C++
大整数运算(高精度运算)C/C++
251 0
二进制浮点数的加减法运算
二进制浮点数的加减法运算
|
存储
5.3.2_原补码的除法运算
计算机组成原理之原补码的除法运算
323 0
5.3.2_原补码的除法运算
|
算法 C语言
5.1.3_无符号整数的表示和运算
计算机组成原理之无符号整数的表示和运算
682 0
5.1.3_无符号整数的表示和运算
35.数值的整数次方
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方
50 0
35.数值的整数次方
|
前端开发 JavaScript 程序员
数值的整数次方
数值的整数次方
数值的整数次方