补码的原理: 为什么 127 + 1 = -128?

补码的原理: 为什么 127 + 1 = -128?#

为什么不在答疑范畴?#

不知道为什么, C++ 课开始时很喜欢谈码制这些东西, 实际上对于 C++ 初期的学习是完全没用的.

在 C++ 中, 有符号数的溢出 (例如此处的 127 + 1 = -128) 是未定义行为, 任何事情都可能发生: 你的软件可能因为这个未定义行为崩溃, 你的电脑可能因为这个未定义行为爆炸.

跟码制同样没用的是记忆基础的 int, long 等类型的精度和存储大小, 这会根据所在平台不同而变化, 因而完全没有意义, 只是在初学者增加负担.

为什么 127 + 1 = -128?#

码制存在的原因是, 计算机的存储是有限的而数字是无限的. 例如此处的 127 + 1 = -128 是由 8 位二进制构成的补码, 即 0000'0000, 总共有 \(2^8\) 种取值可能. 有限的取值可能意味着只能表示一定范围内的数字, 而补码的选择是表示一定范围内的负数、零及正数.

更重要的是, 补码是为了便于计算机计算而产生的, 它在不溢出的情况下, 完美地支持 +1 和 -1 运算; 当溢出时, 超出表示范围的进位/借位可以忽略不计, 即:

1  1111'1111
2+ 0000'0001
3-----------
41|0000'0000 (忽略超出范围的进位 1, 得到 0000'0000)

我们可以借此来推断它如何分配值:

  • 首先规定, 0000'0000 表示零.

  • -1 得到 1111'1111, 这就是 -1.

    为什么是 1111'1111 呢? 这里理解起来比较方便的方法是反过来想: 什么东西 +1 能得到 0000'0000? 上面的例子中已经给出了, 是 1111'1111.

  • +1 得到 0000'0001, 这就是 1.

这样我们便规定, 0000'0000 表示零, 0xxx'xxxx 表示正数, 1xxx'xxx 表示负数.

那么,

  • 最大的正数 (一直 +1) 就是 0111'1111, 即十进制 127.

  • 最小的负数 (一直 -1) 就是 1000'0000, 即十进制 -128.

    这里依然可以反过来理解, 什么东西以 1xxx'xxxx 的格式, 但是需要最多次 +1 才能得到最大的负数 1111'1111.

可以看到, 最大的正数再 +1, 就会得到最小的负数. 因而 8 位二进制补码下, 127 (0111'1111) + 1 = -128 (1000'0000).