原码,反码,补码,移码

张彤 2022年02月20日 1,290次浏览

计算机的底层二进制编码规则。

机器数和真值

  • 一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.
    比如:
00000011 # 表示正3
10000011 # 表示负3
  • 带符号位的机器数对应的真正数值称为机器数的真值
    比如:
0000 0001的真值 = +000 0001 = +1
1000 0001的真值 = –000 0001 = –1

几种码值的概念和计算方法

原码

原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值。

[+1]原 = 0000 0001

[-1]原 = 1000 0001

取值范围

[1111 1111 , 0111 1111] # 原码形式
[-127 , 127]            # 二进制bits形式
  • 原码是人脑最容易理解和计算的表示方式

反码

反码的表示方法是:

  1. 正数的反码是其原码本身
  2. 负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
[+1] = [00000001]原 = [00000001]反

[-1] = [10000001]原 = [11111110]反
  • 可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算.

取值范围

[1111 1111 , 0111 1111] # 原码形式
[-127 , 127]            # 二进制bits形式

补码

补码的表现形式

  1. 正数的补码就是其本身
  2. 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]补

[-1] = [10000001]原 = [11111110]反 = [11111111]补
  • 对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值.

取值范围

[-128 , 127]  # 二进制bits形式,因为加反码负数会产生-0,而补码是反码+1,所以多出一位   

移码

在计算机科学中,移码(英语:Offset binary)是一种将全0码映射为最小负值、全1码映射为最大正值的编码方案。移码没有标准,但通常对于n位二进制数,偏移量K = 2n−1——这使得真值0的编码的最高位为1、其余位均为0,相当于补码表示的最高位(符号位)取反;另外,移码在逻辑比较操作中可以得到和真值比较相同的结果,补码则当且仅当符号相同时逻辑比较操作的结果和真值比较相同,否则比较结果将颠倒(负值比正值大)。

  • 移码一般在浮点运算中有较大优势。
[+1] = [00000001]原 = [00000001]反 = [00000001]补 = [10000001]移

[-1] = [10000001]原 = [11111110]反 = [11111111]补 = [01111111]移

为什么要有反码和补码

上述三类的整数表示方法都相同

[+1] = [00000001]原 = [00000001]反 = [00000001]补

但是负数却各不相同

[-1] = [10000001]原 = [11111110]反 = [11111111]补

既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢?
和人脑不同,计算机实现加减乘除,是靠着电路,如果每种符号位都设计一种电路,在使用的时候,会使得计算机计算变得非常复杂。几种计算的电路设计本文不讨论,有兴趣可以移步 CPU是如何进行运算的
一位半加器的符号
于是人们开始探索 将符号位参与运算, 并且只保留加法的方法。

下面以 计算十进制的表达式: 1-1=0 为例说明

  1. 先是原码计算
1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
  1. 然后是反码
1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0

注意,既然是符号参与计算,那么结果就应该是0前面有负号的

发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在"0"这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.

于是补码的出现, 解决了0的符号以及两个编码的问题:

  1. 补码计算
1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原 = 0

使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].