原码

计算机内的数据都是使用 0 或 1 进行存储,为了引入我们的负数,就用一个数的最高位作为符号位,即用 0 代表正数,1 代表负数,这就是提到的 原码

但是很快就发现了原码在实际使用中的问题:
(1)10(1)10=(1)10+(1)10=(0)10(1)_{10}-(1)_{10}=(1)_{10}+(-1)_{10}=(0)_{10} 在十进制下运算显然正确
但是,假设字长为 8bits,在二进制下
(00000001)Y+(10000001)Y=(10000010)Y=(2)(0 0000001)_Y+(1 0000001)_Y=(1 0000010)_Y=( -2 ) 出现错误

  • 问题出现在带符号位的计算上,由于二进制相加直接按位相加,会导致结果的符号位出现错误。

反码

很快就有了新的解决方案:反码,即对负数除符号位之外的各位逐位取反就产生了与之对应的反码

随之出现了新的问题
(1)10(1)10=(1)10+(1)10=(0)10(1)_{10}-(1)_{10}=(1)_{10}+(-1)_{10}=(0)_{10}
(00000001)R+(11111110)R=(11111111)R=0(0 0000001)_R+(1 1111110)_R=(1 1111111)_R=-0

  • 在我们的认知中,0 不区分正负,这会导致计算出现 0 的情况有两种: +0 和 -0,显然也是错误的

补码

于是又引出了补码的概念:负数的补码就是对其反码加一,正数的原码、反码、补码都是一样,且规定使用-128代替-0

这样就规避了上面提到的问题:
(1)10(1)10=(1)10+(1)10=(0)10(1)_{10}-(1)_{10}=(1)_{10}+(-1)_{10}=(0)_{10}

(00000001)B+(11111111)B=(00000000)B=(0)(0 0000001)_B+(1 1111111)_B=(0 0000000)_B=(0) 正确。

(1)10(2)10=(1)10+(2)10=(1)10(1)_{10}-(2)_{10}=(1)_{10}+(-2)_{10}=(-1)_{10}

(00000001)B+(11111110)B=(11111111)B=(1)(00000001)_B+(11111110)_B=(11111111)_B=(-1) 正确。

所以补码的设计目的

  • 使符号位能与有效值部分一起参加运算,从而简化运算规则。补码机器数中的符号位,并不是强加上去的,是数据本身的自然组成部分,可以正常地参与运算。

  • 使减法运算转换为加法运算,进一步简化计算机中运算器的线路设计。