zoukankan      html  css  js  c++  java
  • 深入理解原码,反码,补码的原理

    预备知识

    二进制,十六进制,二进制与十进制的转化运算

    根据冯诺依曼结构的运算器,只有加法运算器,没有减法运算器

    所以,计算机中不是直接做减法,是通过加法来实现的。所以就必须引入一个符号位

    原码,反码,补码 的产生就是为了解决这个问题

    原码

    最简单的机器数表示法

    原码:

    最高位表示符号位,1表示负,0表示正

    其他位存放该数的二进制的绝对值

    直接用原码运算

    0001+0010=0011    (1+2=3)正确
    0000+1000=1000    ((+0)+(-0)=-0)
    0001+1001=1010    (1+(-1)=-2)出错
    1001+1001= ?
    

    原码正数之间的加法通常不会出错

    正数与负数相加,或负数与负数相加,问题就出现了

    所以,原码直观易懂,易于正值转换,但用来实现加减法的话,运算规则总归是太复杂,于是反码来了

    反码

    原码最大的问题就在于一个数加上他的相反数不等于零,反码的设计思想就是为了解决这一点

    反码:

    正数的反码=原码

    负数的反码=原码除符号位外,按位取反

    我们试着用反码运算

    0001+1110=1111 (1+(-1)=(-0))  有点问题,也正确
    1110+1101=1011 (-1)+(-2)=(-4) 两个负数相加,出错
    

    负数相加出错,问题不大,我们只需要在两个负数相加时,将两个负数反码包括符号位全部按位取反相加,然后再给他的符号位强置‘1’就可以了。

    反码表示法其实已经解决了减法的问题,他不仅不会像原码那样出现两个相反数相加不为零的情况,而且对于任意的一个正数加负数,计算结果是正确的

    然后就有了补码

    补码

    补码:

    正数的补码=原码

    负数的补码=反码+1

    负数的补码的另外一种算法:

    自低位向高位,尾数第一个1及其右部的0保持不变,左部取反,符号位不变

    其实上面两段话,都只是补码的求法,而不是补码的定义,基础工作者并不会心血来潮的把反码+1就定义为补码,只不过是补码正好就等于反码加1罢了

    暂时先忘记书上那句负数的补码等于它的反码+1,我们的理解陷入了误区

    这也是为什么《计算机组成原理》要特意先讲补码,再讲反码

    接下来我们要重点讲讲补码的思想

    补码的思想

    为了理解,先得引入同余的思想

    其实就是一个计量器的容量大小,比如钟表的模M=12

    同余 是指两个整数A和B除以同一个正整数M,所得余数相同,比如1点和13点,2点和14点就是同余的,可以写作

    1 = 13 mod (12), 2 = 14 mod (12)
    

    如果说现在时针现在停在10点钟,那么什么时候时针会停在8点钟呢?

    这么说吧,因为过去2个小时前是8点,所以未来10个小时候也是8点

    也就是说:倒拨2小时 或 正拨10小时 都是八点钟

    10-2=8,(10+10)mod(12)=8
    

    所以,10-2和10+10从另一个角度来看是等效的,它都使时针指向了8点钟

    既然是等效的,那在时钟运算中,减去一个数,其实就相当于加上另外一个数(这个数与减数相加正好等于12,也就是同余数)

    我再次强调,原码,反码,补码的引入是为了解决做减法的问题。在原码,反码表示法中,我们把减法化为加法的思维是减去一个数,等于加上一个数的相反数,结果由于引入符号位造成了各种问题

    那你应该知道我要说什么了,利用模和同余的概念,我们可以使减法运算转化为加法运算

    而现在,我们不引入负数的概念,就可以把减法当成加法来算

    所以接下来我们聊4位二进制数的运算,也不必急于引入符号位。因为补码的思想,把减法当成加法时并不是必须要引入符号位的。

    而且我们可以通过下面的例子,也许能回答另一个问题,为什么负数的符号位是‘1’,而不是正数的符号位是‘1’

    四位二进制补码运算实例

    0110 - 0010 (6-2=4) 计算机中没有减法器,不能直接算
    

    但是现在你知道,减去一个数,可以等同于加上另外一个正数(同余数)

    那么这个数是什么呢?时钟运算中我们可以看出这个数与减数相加正好等于模M=12

    四位二进制数的(计量器)=四位二进制数最大容量=2^4=16=10000B

    那么2(0010)的同余数,就等于10000-0010=1110(14)

    既然如此

    0110(6) - 0010(2) = 0110(6) + 1110(14) = 10100(16+4=20)
    

    OK,我们看到按照这种算法得出的结果是10100,但是对于四位二进制数,最大只能存放4位(硬件决定),正好是0100(4),就是我们想要的结果,至于最高位的 1,计算机会把他放入psw寄存器进位位中。8位机则会放在cy中,x86会放在cf中(不作讨论)

    这个时候,我们再想想在四位二进制数中,减去2,就相当于加上它的同余数14

    但是减去2,从另外一个角度来说,也是加上(-2)。即加上(-2)和加上14其实得到的二进制结果除了进位位,结果是一样的。

    如果我们把1110(14)的最高位看作符号位后就是(-2)的补码,这可能也是为什么负数的符号位是‘1’而不是‘0’,

    而且在有符号位的四位二进制数中,能表示的只有‘-8~7’,而无符号位数(14)的作用和有符号数(-2)的作用效果其实是一样的。

    那正数的补码呢?加上一个正数,加法器就直接可以实现。所以它的补码就还是它本身。

    到这里,我们发现原码,反码的问题,补码基本解决了。

    做减法时,0001(1)+1111(-1)=0000,我们再也不需要一个1000来表示负0了,就把1000规定为-8

    负数与负数相加的问题也解决了1111(-1)+1110(-2)=1101(-3)

    为什么可以+1

    然后我们再来看看为什么负数的补码的求法为什么是反码+1

    因为负数的反码加上这个负数的绝对值正好等于1111,再加1,就是1000,也就是四位二进数的模

    而负数的补码是它的绝对值的同余数,可以通过模减去负数的绝对值,得到他的补码。

    所以,负数的补码就是它的反码+1

    算补码的小技巧

    如果我们把-8当成负数的原点。那么-5的补码是多少呢?

    -5 = -8 + 3
    

    -5的补码就是-8的补码加3

    1000(-8) + 0011(3) = 1011(-5)
    

    也可以记住-1的补码是1111口算减法得出

    1111(-1) - 0100(4) = 1011(-5)
    

    对于八位加法器的话,可以把-128当补码原点。十六位可以把-32768当补码原点。

    是的,128256(八位二进制数的模)的一半,3276865536(十六位二进数的模)的一半

    也很方便有没有,而且简单的是

    补码原点总是最高位是‘1’,其他位是‘0’

    感谢阅读

  • 相关阅读:
    在win7系统下安装把Ubuntu17.04安装在另一个硬盘开机无法进入Ubuntu问题的一种解决办法。【转】
    快速上手Ubuntu之安装篇——安装win7,Ubuntu16.04双系统【转】
    win7 64位系统与Ubuntu14.04 64位系统双系统安装【转】
    kernel中对文件的读写【学习笔记】【原创】
    快速解决Android中的selinux权限问题【转】
    Android如何配置init.rc中的开机启动进程(service)【转】
    linux内核驱动中对文件的读写 【转】
    很好的 DHCP协议与dhcpcd分析【转】
    android DHCP流程【转】
    Android wifi 从连接态自动断开的解决办法(dhcp导致)【转】
  • 原文地址:https://www.cnblogs.com/zhxmdefj/p/10902322.html
Copyright © 2011-2022 走看看