上篇文章讲了 "负数在计算机中是怎么存储的". 看完之后, 应该对原码, 反码, 补码有了基本的了解了.
今天, 我们深入探讨一下, 为什么计算机中要用补码来表示负数?
首先, 我们应该清楚, 原码是方便给人看的. 看到一个数的原码, 我们就能根据符号位和后边的二进制位, 计算出这个数的实际值. 为了简单起见, 我以一个字节 8 位来举例, 如
- // 1 的原码 , 最高位 0 代表正数
- 0000 0001
- // -1 的原码, 最高位 1 代表负数
- 1000 0001
可以看到, 1 和 -1 的原码只有符号位不同. 然后, 思考一个问题, 1 - 1 = ?
是的, 我们可以直接通过减法去计算, 得出 1-1=0 . 但是, 做减法运算时, 可能会遇到不够减而需要借位的情况, 这显然是比较麻烦的. 我们换一种思路. 1-1 在数学中等同于 1+(-1). 这样, 把减法转换为加法就简单的多了, 只需要考虑进位就可以了.(其实, 计算机中只有加法器, 没有减法器, 因此减法是通过加法器来计算的.)
于是, 我们看下, 把 1 和 - 1 的原码相加等于多少 (需要让符号位也参与运算)
- 0000 0001
- + 1000 0001
- 1000 0010
结果是 -2 , 这显然不符合我们的预期.
为了解决原码减法的问题, 于是, 出现了反码. 使用反码, 再来计算一下.
- // 1 的反码, 同原码
- 0000 0001
- // -1 的反码, 符号位不变, 其他取反
- 1111 1110
相加之后, 得 1111 1111 , 这是反码, 转为原码为 1000 0000 , 即为 -0 .
但是, 这又有问题了, 在数学中 0 就是 0, 怎么到这还有 -0,+0 之分. 按照原码的概念来算,+0 的原码为 0000 0000 , -0 的原码为 1000 0000 . 问题就出在这了, 如果遇到 0 的计算, 是应该用 +0 还是用 -0 计算呢, 这就会产生分歧. 于是, 补码出现了, 解决了 0 的符号问题 .
- // 1 的补码, 同原码
- 0000 0001
- // -1 的补码, 反码 +1
- 1111 1111
相加得 1 0000 0000 , 最高位进位之后, 超过了 8 位, 于是舍去, 即为 0000 0000. 此为补码, 转为原码也是 0000 0000 , 这不就是 0 吗.
这样一来, 用补码 0000 0000 来表示 0, 就解决了 + 0 和 - 0 在原码上的分歧, 统一了 0 的二进制表示方法.
那, 又有疑问了,-0 跑哪去了呢? 其实,-0 即 1000 0000 在这用来表示 -128. 但是, 注意表示的是 -128 的补码, 因此 -128 没有原码和反码.
那为什么用 1000 0000 表示 -128 呢 ?
先看下 -127 的原码, 反码和补码:
原码: 1111 1111
反码: 1000 0000
补码: 1000 0001
我们知道数学中 -127 -1 = -128 , 所以 -127 的补码 -1 也应该等于 -128 的补码, 即
1000 0001 -1 = 1000 0000. 因此 1000 0000 就是 -128 的补码.
在一个字节 8 位中, 如果用原码来表示值的大小范围, 只能是 1111 1111 ~ 0111 1111, 即 - 127~127 . 但是, 如果用补码就可以表示 -128~127, 正好是 2^8,256 个数.
因此,-0 可以表示一个最低数. 在 8 位二进制中它是 1000 0000 , 在 32 位中, 它就是 1000 0000 0000 0000 0000 0000 0000 0000 ,int 的最小值.(32 位数值大小范围为 -2^31 ~ 2^31 -1)
总结: 补码的存在解决了 0 的符号问题, 同时统一了计算机的加减法运算.
来源: https://www.cnblogs.com/starry-skys/p/11997091.html