深入理解计算机系统(3)

本文我们主要讲关于数据的的表示方式:原码,反码和补码。

本文在写作过程中,参考了园中的这篇文章《原码,反码,补码详解》,特此声明。

 

一原码

计算机中是使用二进制来表示数据的,对于C语言这样的强类型语言,每一个数值类型,都有其范围,例如一个int类型,在32位机器上,其表示的范围如下:

 

最小值

最大值

有符号整数

-2147483648

217483647

无符号整数

0

4294967295

而如果我们定义了一个int类型,给其的赋值,超出了这个范围,则会出现问题。一般编译器会检查这个问题,会出现编译错误。

 

为了简单,我们以一个char类型(8位)来进行举例:

 

最小值

最大值

有符号char

-128

127

无符号char

0

255

 

那么什么是原码呢:原码是二进制的一种表示方式,其规特点是:

无符号数的每一位都是数字位,

有符号数的最高位是符号位,0表示正数,1表示负数,其余各位表示数值。

1.1无符号数的原码

先说无符号的情况,我们选择几个值

十进制

二进制原码

0

00000000

127

01111111

128

10000000

255

11111111

可见最高位为1,表示十进制的数字128,8位全部都是1,表示255。

 

例如计算两个数字1和1相加:

 

十进制

无符号二进制原码表示

加数1

1

00000001

加数2

1

00000001

求和

2

00000010

 

因此,对于一个无符号数,使用原码表示是正常的。

 

1.2有符号数的原码

对于有符号数,用原码表示负数,要怎么表示呢?

因为是二进制,也就只有0和1两种符号,于是就定义最高位为符号位:最高位为0的原码,表示一个非负数;最高位为1的原码,表示一个负数。

 

十进制

无符号原码

有符号原码

0

00000000

00000000

127

01111111

01111111

-127

超出范围,无法表示

11111111

255

11111111

超出范围,无法表示

由上面的表格,我们可以发现原码表示的特点:

1对于一个非负数,在不超出其表示范围的前提下,无符号数和有符号数的原码表示形式是一样的;

2对于一个负数(有符号数),在不超出其表示范围的前提下,其最高位为1,而其余各位与无符号数一样。

 

那么如果我们在进行计算的时候,使用原码来对两个有符号数进行二进制加法运算会怎么样呢?

两个数都非负数,计算结果是是正确的;

如果有一个数是负数,就会出现问题:

例如:计算如下的两个数字,1和-1相加:

 

十进制

有符号二进制原码表示

加数1

1

00000001

加数2

-1

10000001

求和

-2

10000010

 

1+(-1) = -2

这显然,不是我们想要的结果。

 

如果想用原码表示,就需要计算机对负数的原码进行特殊的处理。如上面的例子,加数2的值是10000001,那么最高位不参与计算,只用于标记这是一个负数。然后将一个正数和另一个负数的加法,转变为两个正数的减法。

即将1+(-1)变为1-1,按照二进制的减法运算法则,得到

 

十进制

有符号二进制原码表示

被减数

1

00000001

减数

1

00000001

求差

0

00000000

 

1-1=0

这样就得到了,我们想要的结果。

这样看起来似乎也是可以接受的,那我们再算一些其他的数字,例如1+(-2),按照上面的规则,我们要将其变为1-2

 

十进制

有符号二进制原码表示

被减数

1

00000001

减数

2

00000002

求差

?

?

由于1小于2,因此1减2,等于2减1,再对结果求其相反数。根据减法的规则,1-2= - (2-1)

 

十进制

有符号二进制原码表示

被减数

2

00000002

减数

1

00000001

求差

1

00000001

求反

-1

10000001

这样我们也得到了,正确的结果。

但是我们要注意以下几点问题:

1要判断运算数中是否有负数

2可能将加法运算变为减法运算

3可能要求其相反数

可见,本来一个简单的加法的运算,由于负数的问题,变得复杂了。而要处理这些问题,计算机需要一些额外存储空间,以及额外的计算步骤。相应的电路的设计就要更加复杂。

结论:复杂度高,不适合。

二反码

如果使用原码可以很方面的进行计算的话,我也就不会使用反码的方式来表示二进制了。

从上一节可以发现,使用原码的方式在做负数的加法时,比较麻烦。

而负数,表示的是一个正数的相反数,那么我们将一个二进制数按位取反,会有怎样的现象呢?

我们看以下这些数字。

原始值

无符号数原码

原码按位取反

去反后的值

0

00000000

11111111

255

127

01111111

10000000

128

255

11111111

00000000

0

 

可以看出,无符号数,二进制按位取反,其结果为原值被最大值(255)减,即:

0按位取反表示255 – 0 = 255

127按位取反表示 255 – 127 = 128

255按位取反表示 255 – 255 = 0

 

那么对于有符号数有是什么样呢?我们看以下有符号数字,原码符号位不变,数字位按位取反的计算结果

原始值

有符号数原码

原码按位取反

取反后的值

-127

11111111

10000000

-0

-3

10000011

11111100

-124

-1

10000001

11111110

-126

0

00000000

01111111

127

1

00000001

01111110

126

3

00000011

01111100

124

127

01111111

00000000

0

可以看出,有符号数,除了符号位,二进制按位取反,其结果的绝对值为原值被最大值(127)减,即:

-127按位取反表示- (127 – 127 ) = -0

-3按位取反表示 – (127 – 3 ) = - 124

0按位取反表示 +(127 – 0 )= 127

3按位取反表示+(127 – 3) = 124

127按位取反表示+(127 – 127 )= 0

 

 

 

那么如果我们在计算有符号数加法时,我们可以得到如下的四种情况

 

使用原码

使用原码取反

正数

 

 

负数

 

 

 

我们按照这四种情况分别计算1 + (-1),使用二进制加法,看看有什么有趣的现象

 

1正数使用机器码,负数使用机器码

 

在计算符号不同的两个数值的加法时,最终结果的符合与绝对值大的那个相同。

但是如果两个数值不为0,且互为相反数,那最终的结果的符号就不一定了。

 

 

十进制

有符号二进制原码表示

加数1

1

00000001

加数2

-1

10000001

求和

x0000010

x=0

2

00000010

X=1

-2

10000010

无论x是正还是负,结果似乎都不正确。

 

 

2正数使用机器码,负数使用机器码按位取反

 

 

 

十进制

有符号二进制原码表示

加数1

1

00000001

加数2(按位取反)

-2

11111101

求和

-1

10000001

我们定义最高位符号位的值是x,

对于1 + (-2) ,由于负数的绝对值(2)大于正数的绝对值(1),因此可以确定,x的值是1。

也就是首先能确定最终结果是个负数,然后计算其后面的数值位,

七位的:0000001

加上

七位的:1111101

得到

七位的:1111110

再加上符号位,得到

八位的:11111110

按照前提,这是一个负数,其真值要对其数值为按位取反,得到10000001,即 -1。

这个结果,貌似很好。但是如果计算1+ (-1)会怎么样呢?

 

十进制

有符号二进制原码表示

加数1

1

00000001

加数2(按位取反)

-1

11111110

求和

??

X1111111

X=0

127

01111111

X=1(按位取反)

-0

10000000

 

这里在计算00000001和11111110时,由于1和-1的绝对值相等,那么其符号就无法确定。那么我们先不考虑符号位,将数值位进行二进制加法,然后得到x1111111。

那么这时候,x的值有两种可能:

X = 0 ,那这个数字是正数,正数值为:127

X = 1,那这个数字是负数,负数在本环境下是要按位取反来得到其真值的,因此

11111111按位取反,得到10000000,即真值为- 0

那么到底x等于几呢?这个似乎也是个不太好说的问题。

对于 -0 和 127,貌似 -0更加贴近最终的答案,那可以人为的规定,如果负数按位取反,在计算时,符号位优先使用1,即x值无法确定时,优先取1。

 

我们再计算一下-1 + (-1)

 

 

十进制

有符号二进制原码表示

加数1(按位取反)

-1

11111110

加数2(按位取反)

-1

11111110

求和

-124

11111100(溢出)

按位取反

-3

10000011

 

 

3正数使用机器码按位取反,负数使用机器码

 

 

十进制

有符号二进制原码表示

加数1(按位取反)

2

01111101

加数2

-1

10000001

求和

126

01111110

按位取反

1

00000001

 

 

 

 

十进制

有符号二进制原码表示

加数1(按位取反)

1

01111110

加数2

-1

10000001

求和

??

X1111111

X=0(按位取反)

0

00000000

X=1

-127

11111111

 

根据第2中情况的分析,我们可以得到上表的结果。

此时,x的值仍然有两种情况,

X优先取0,则得到结果 0

X优先取1,则得到结果-127

那么这种情况下可以规定,x值优先取0。

 

我们计算1 + 1

 

十进制

有符号二进制原码表示

加数1(按位取反)

1

01111110

加数2(按位取反)

1

01111110

求和

124

01111100(溢出)

按位取反

3

00000011

 

 

4正数使用机器码取反,负数使用机器码取反

 

 

十进制

有符号二进制原码表示

加数1(按位取反)

1

01111110

加数2(按位取反)

-1

11111110

求和

??

X1111100(溢出)

X=0(按位取反)

3

00000011

X=1(按位取反)

-3

10000011

 

 

归纳上面的规律

 

序号

名称

1+1

2+2

1+(-1)

1+(-2)

-1+(-1)

-2+(-2)

1

正原负原码

2

4

2或-2

-3

-2

-4

2

正原负反码

2

4

-0或127

-1

-3

-1

3

正反负原码

3

1

0或-127

-0

-2

-4

4

正反负反码

3

 

3或-3

 

-3

 

 

 

 

可以看到,有价值的是1和2,将两种组合,应该可以计算出正确的结果。

 

 

正+正

正+负

负+负

结论

1

Y

N

Y

符号相同成立

2

Y

Y

N

符号不同成立,双正成立

 

 

我们使用1和2结合,得到如下二进制加法计算规则:

 

在符号相同时,符号位不变,数值位直接进行二进制加法。

在符号不同时,符号位由绝对值大的数字的符号决定,如果绝对值相同,优先使用负值,而负数将数值部分,用机器码按位取反后,再与正数进行二进制加法。

 

那么按照这个规则,在进行计算机的运算处理时,需要考虑哪些问题呢?

 

1要判断数字的符号

2要根据符号相同和不同采取不同的策略

 

这里面,1是必须要考虑的,因为有符号数字,必须要有一个位来标识符号。

有了这一点,就可以解决同符号的加法问题了。

 

对于不同符号的加法,如果正数和负数绝对值相同,仍然是个不好处理的问题。

结论:复杂度低,但是还有待改善

 

三补码

上节讲的,正数用机器码表示,负数用机器码按位取反的表示方法,就是反码。

而且,一个数字的反码所表示的数字,与自身的和,是固定值——这个数值所能表示的最大值。

对于8位无符号数值类型的变量,其最大值是255。

而7位的话,最大值是127。

因此,对于1+(-2),可以先确定符号位是1,然后计算000 0001和000 0010(取反)的和

即,000 0001 + 111 1101,结果是111 1110。再取反,得到000 0001。再补上最高位的1

得到,1000 0001,结果就是-1。

 

当正值和负值的绝对值相同的时候,例如 1+(-1),我们规定此时符号0,然后计算

000 0001和000 0001(取反)的和,即000 0001 + 111 1110,结果是111 1111。由于这个数字式正数,因此其值为127。注意,这里的前提是,正数保持原样,负数按位取反。

那么127,代表什么呢?

对于一个char字符,除去其最高位符号位,还剩下7位。

7位二进制的表示范围是000 0000到111 1111,即0到127,共128个数字。

从000 0000开始加1,加127次达到最大值。

达到最大值之后,如果再加1,就得到1 000 0000。而这里的最高位因为溢出而会被舍去,因此只存储了000 0000这7位值,这个值就回到了最初。

 

对于钟表来讲,从1开始,依次加1,加到12时,达到了最大值。再加1的话,就得到了13,而13由于溢出,因此只保留了1。

 

因此,我们可以得到这个结果,如果最小值从1开始,那么最大值就是周期。

因此钟表的周期是12。

那么7位二进制数的周期是多少呢?从0到127,为了和钟表统一,我们将其整体加1:

从1开始,最大到128。这样可以推出,7位二进制的周期是128。

 

也就是说,7位二进制数,表示的数字,具备这样的特性。

数字N与其按位取反的数字M,必有N+M=周期-1。

因此N=周期-1-M。

而周期会溢出,因此在N上增加若干整数倍周期,与没有增加周期,其结果是一样的。

因此有:周期+N = 周期 – 1 – M。注意这里的相等,是去掉溢出位之后相等。

那么所有两边都去掉一个周期。

就得到

N = -1 – M即N=  -(M+1)

因此就有 -N =  M + 1 

 

根据同余的特性,对于任意数字X,X在7位二进制数字表示范围内:

有X+(-N) =  X+ M+1

而M为N的按位取反(反码)。所以对于一个负数,在其二进制反码的基础上在加上数字1,就得到了其同余的数值。这就是现在计算机中实际表示数据的形式——二进制的补码。

 

以上的推导过程,不是严格的数学证明,只是为了方便理解。

 

而我们用补码方式来进行计算1 + (-1)

首先确定符号位的值是0

因此计算000 0001 和 000 0001(求补)的和,即

000 0001 + 111 1111 因此,得到000 0000,再补充上最高位0,

得到0 000 0000,即数字0。

完美解决了,在使用反码时,正数情况下的127而负数情况下-0的问题。

 

结论:使用补码存储数值,可以用加法的运算处理减法。计算相反数的加法,也正确。

可以使用。

 

四总结

数学是计算机的基础,从计算机的二进制存储可见一斑。因此我们在学习计算机技术的同时,加强数学知识的学习,是非常重要的。

 

posted on 2016-06-04 18:09  Sempron2800+  阅读(478)  评论(0编辑  收藏  举报