zoukankan      html  css  js  c++  java
  • 深入理解计算机系统(3)

    深入理解计算机系统(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的问题。

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

    可以使用。

    四总结

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

  • 相关阅读:
    actionBar-进入界面闪烁问题解决
    softInputMode- 软件盘的设置
    LinearLayout -设置负值属性
    launcher- 第三方应用图标替换
    resource-color 的引用
    java学习笔记——IO流部分
    二进制基础
    java学习笔记——IO部分(遍历文件夹)
    Java线程:线程的同步与锁
    AWT与Swing的区别
  • 原文地址:https://www.cnblogs.com/asenyang/p/5559236.html
Copyright © 2011-2022 走看看