写在前面
大家好,这里是睿智的仓鼠。
在我自学Java的过程中,整理了很多学习笔记,最近在着手开源给大家。如果您发现文中的不恰之处,欢迎私信或评论提出宝贵意见!

下面开始今天的正题:为什么计算机的设计者要提出原码、反码和补码,直接把十进制转化成二进制数使用不行吗?
1、夭折的三进制
计算机科学的探索阶段,美国的主流是二进制,而苏联把精力都花在了研究“三进制”上。
三进制有三种状态:-1、0、1,对应正负电压和零电压。
它的好处是可以很方便地直接表示“负数”这个概念,如果三进制流行至今,可能就不会有原码、反码、补码这些概念了。
2、计算机如何表示负数
计算机中的二进制数,第一位是符号位。正数符号位为0,负数为1。为什么能这样做,后面做了介绍。
什么是原码
一个数在计算机中最基本的二进制表达(拥有符号位机制),就是这个数的原码。
- 正数:原码就是它的二进制数
- 负数:原码是它的二进制数,高位为1
什么是反码
正数的原码、反码、补码是一致的,这个机制是为了表示和计算负数。
负数的补码,是除了符号位之外,其余位都取反的结果。例如:

什么是补码
负数的补码,是除了符号位之外,其余位都取反,再+1的结果。也就是反码+1
计算机内部,负数是以补码的形式存在的
。这带来一个好处:不管是加法还是减法,本质上计算机都只需要做加法即可。
3、计算机如何计算减法
早起的计算机只有加法器,要做减法,就必须做一些特殊处理,由此诞生了补码和反码。
计算机处理加法比较容易,因为加法只需要考虑“进位”。但减法需要考虑“借位”的问题,如果用电子电路来模拟减法,会非常麻烦。
有一个小技巧,可以避免减法的借位
例如 253 - 176 = 77,涉及到多次借位,例如3借位变成13
- 我们可以首先计算176的“补数”:从999中减去176,得到823。这里使用999,是因为被减数有三位。(任何被减数与同等位数的9相减,绝对不会发生借位)
- 然后,将补数 823 和原来的被减数 253 相加,得到1076
- 将结果+1,再减去1000,得到77。这就是答案。
为什么这样可行?因为操作等同于 253 + (999 - 176) + 1 - 1000
对于二进制数,这种方法更加简单
- 将上面的式子化为二进制,得到1111 1101 - 1011 0000
- 计算被减数对1的补数,即0100 1111
- 将补数与被减数相加 1111 1101 + 0100 1111 = 1 0100 1100
- 结果+1,得到1 0100 1101
- 减去1 0000 0000,得到0100 1101,结果等于十进制的77
可以看到,我们已经实现了完全用加法来代替减法。
实际上,不需要进行去高位的操作
计算机内部,负数是以补码的形式存在的。
上面这套理论用在计算机上,有一个前提,那就是数字的位数是已经确定的,才能让最高位成为符号位。
在位数已经确定的情况下,如果发生了上溢,就相当于去掉了高位,结果自然是正确的,非常巧妙。
此处的内容,在《编码:隐匿在计算机软硬件背后的语言》一书中做了详细介绍,非常推荐阅读
看到这里,相信你已经有了一些感觉:原来补码的出现,是为了简化计算机内部的减法运算。