SouthAki的个人理解

一文读懂原码、反码与补码

  • 二进制
    二进制和十进制一样,也是一种进位计数制,但是它的基数是 2。二进制表达式中 0 和 1 的位置不同,它所代表的数值也不同。例如,二进制数 0000 1010 表示十进制数 10。一个二进制数具有两个基本特点:两个不同的数字符号,即 0 和 1,逢二进一。

    十进制与二进制数之间的转换
    用计算机处理十进制数时,必须先把它转化为二进制数才能被计算机所接受;同理,计算结果应该将二进制数转换成人们习惯的十进制数。
    十进制转换成二进制
    把一个十进制转换为二进制的方法是:把被转换的十进制数反复地除以 2,直到商为 0 为止,所得余数(从末位读起)就是这个数的二进制表示,简单地说,就是 “除 2 取余法”。
  • 小技巧
    这里给一个技巧,就是8421,每个数去算是那个相加起来等于十进制数,你就按位去给原码0上加1

正文

为运算方便,机器数有 3 种表示法,即原码、反码和补码。

  • 原码
    原码是一种计算机中对数字的二进制定点表示法。原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为 0,负数该位为 1(0 有两种表示:+0 和 -0),其余位表示数值的大小。举个例子,我们用 8 位二进制表示一个数,+12 的原码为 00001100,-12 的原码就是 10001100。
  • 反码
    一个数字用原码表示是容易理解的,但是需要单独一个位来表示符号位,并且在进行加法时,计算机需要先识别某个二进制原码是正数还是负数,识别出来之后再进行相应的运算。这样效率不高,能不能让计算机在进行运算时不用去管符号位,也就是让符号位参与运算。要实现这个功能,我们就要用到反码。
    反码是一种在计算机中数的机器码表示。对于单个数值(二进制的 0 和 1)而言,对其进行取反操作就是将 0 变为 1,1 变为 0。正数的反码和原码一样,负数的反码就是在原码的基础上符号位保持不变,其他位取反。
  • 补码
    补码是一种用二进制表示有符号数的方法。正数和 0 的补码就是该数字本身。负数的补码则是将其对应正数按位取反再加 1。补码系统的最大优点是可以在加法或减法处理中,不需因为数字的正负而使用不同的计算方式。只要一种加法电路就可以处理各种有符号数加法,而且减法可以用一个数加上另一个数的补码来表示,因此只要有加法电路和补码电路即可以完成各种有符号数加法和减法,在电路设计上相当方便。
    另外,补码系统的 0 就只有一个表示方式,这和反码系统不同(在反码系统中,0 有两种表示方式),因此在判断数字是否为 0 时,只要比较一次即可。下图是一些 8 位补码系统的整数,它可表示的范围包括 -128 到 127,总共 256 个整数。

这张图也很好用

以上的如果看不懂,没关系,因为官方的话术从来是让人看不懂,但是下面是我的解释

  • 引入下历史背景
    任何数据都是以二进制存储在计算机中。
    根据冯·诺依曼提出的经典计算机体系结构框架,一台计算机由运算器、控制器、存储器、输入和输出设备组成。其中,运算器只有加法运算器。
    计算机虽然没办法做减法,但是可以加上这个数的相反数呀。但是二进制我们不能给它加上负号,于是我们需要引入一个符号位,存储在最左边的一位上,0代表正数,1代表负数。例如一个四位二进制数,最左边是符号位。0001,表示他是+1,1001,表示他是-1。
  • 原码
    想象你是当年设计计算机的科研人员。你把带符号位的四位二进制数做运算,向全世界展示你精妙绝伦的设计—符号位。
    于是你开始了计算——
    • 0001+0010=0011 1+2=3
    • 1000+0000=1000 (-0)+0=(-0)
    • 0101+1010=1111 5+(-2)=-7
      你突然发现,正数加正数没有问题,但是后面的测试出现了问题
    1. 怎么会有两个0呢?
        因为1000和0000都表示 零
    2. 怎么正数加负数会出现问题呢?
        因为符号位引起的。
      总结一下:
      1、原码直观
      2、正数相加没问题
      3、0有两种表达,运算时需要将-0转换为0,也就是1000转换为0000
  • 反码
    正数的反码不变。
    负数的反码:符号位不变,将原码取反。
    原码000的反码就是0110
    然后我们试一试用反码计算一下刚才没有解决的问题
    • 0010 + 1101 = 1111 1+(-1)=0 把1111取反码,1000,也就是说两个相反数相加取反码结果没有问题
    • 1110 + 1011 = 1001 (-6)+(-3)=(-6)取反得到1110, 这样算出来的是-6,出错了
      总结一下:
    1. 反码在计算相反数相加的时候不会出错(计算的时候会遇到运算位溢出,此时直接忽略高位即可)
    2. 0还是有两种表达0000和1111
    3. 容纳数字的范围和原码相同(如上图,原码与反码集合存在映射关系)
    4. 往往是中间变换量,不会直接用
  • 补码
    正数的补码不变。
    负数的补码等于反码+1。
    我们需要注意的是,目前大多数书籍只介绍了补码如何计算,但是都没有讲清楚为何是这样计算,以及这样计算的依据。
    初学的时候很多疑惑。关于补码的严格定义,可以自行百度,里面会介绍模与同余数的概念。
    在此简单介绍一下,举一个生活中常见的12进制的例子来说明模的概念:
    假如当前有一个时钟,指针指向9,如果我要调到12点,我们有两个方法,+3或者-9.
    也就是说凡是-9的运算都可以看做是+3的运算。

    这个情况下,模是12(mod)
    思考到这里时请放缓脚步,慢慢思考。
    我们会发现,9+3居然和9-9在某种意义上的效果是一样的。
    既然两者效果相同那么一个数a减去一个数b就相当于加上这个数b的同余数。
    于是推出模的一般公式:a-b=a-b+mod=a+mod-b
    利用这个思想,我们把它带到二进制的世界里(假设是四位的二进制)
    我们试图运算0011 - 0010 = 0001,但是我们发现计算机中没有减法器,不能算。
    我想便利用上面的思想,减去一个数等于加上一个数的同余数,也就是0011 加上0010的同余数即可
    四位二进制的模 10000,那么0010的同余数就是10000-0010=1110,
    那么我们就直接0011+1110=10001,但是我们是四位的运算,多出的一位会被直接舍弃,计算机会把多出来的一位放在psw寄存器中,不讨论。
    那么至此,我们就可以利用补码计算加法和减法了。
    想必你有疑问,怎么求一个数的补码呢?我们经过大量计算发现,补码竟然是原码的反码+1,非常不可思议。
    为什么会这样呢?
    总结:因为负数的反码加上这个负数的绝对值正好等于1111,在加1,就是10000,也就是四位二进数的模,而负数的补码是它的绝对值的同余数,可以通过模减去负数的绝对值得到它的补码,所以负数的补码就是它的反码+1。