因为C++中的有符号整数通常采用“二进制补码”表示法,而这种方法使得负数的范围比正数的范围大1。

下面我们来详细解释一下。

1. 核心原因:二进制补码

现代计算机几乎 universally 使用 二进制补码 来表示有符号整数。这种表示法有以下几个关键点:

  • 最高位是符号位:在16位整数中,最高位(第15位)为0表示正数,为1表示负数。

  • 非负数的表示:和非负整数(包括0)的表示方式完全一样。

    • 0 表示为 0000 0000 0000 0000

    • 1 表示为 0000 0000 0000 0001

    • ...

    • 32767 表示为 0111 1111 1111 1111(这是最高位为0时能表示的最大值)

  • 负数的表示:一个负数的补码是通过其对应正数的二进制表示“取反(所有位0变1,1变0)再加1”得到的。

    • 例如,求 -1 的表示:

      1. 1 的二进制: 0000 0000 0000 0001

      2. 取反: 1111 1111 1111 1110

      3. 加1: 1111 1111 1111 1111 -> 这就是 -1

    • 求 -32768 的表示:

      1. 32768 的二进制是 1000 0000 0000 0000(注意,这已经是一个17位的数了,对于16位系统,32768本身是无法直接表示的)。

      2. 这里有一个特殊情况。在16位补码中,模式 1000 0000 0000 0000 被直接定义为 -32768

2. 数值范围的计算

一个16位的二进制数,总共有 2^16 = 65536 种不同的组合。

在补码规则下,这些组合被分配如下:

  • 正数:从 0 到 32767

    • 数量: 32768 个 (包括0)

  • 负数:从 -1 到 -32768

    • 数量: 32768 个

所以,总范围是 -32768 到 32767

3. 为什么不能表示 +32768?

关键在于 0 占用了一个编码。如果我们试图表示 +32768,它的二进制应该是什么?

  • 最大的正数 32767 的二进制是 0111 1111 1111 1111

  • 要得到 +32768,理论上需要将最高位变为1,即 1000 0000 0000 0000

但是,在16位补码体系中,这个二进制模式 1000 0000 0000 0000 已经被规定为 -32768 了!

你可以验证一下补码的规则:

  1. 如果 1000 ... 0000 是 +32768,那么它的补码(取反加一)应该是 -32768

  2. 对 1000 ... 0000 取反得到 0111 ... 1111

  3. 加1后得到 1000 ... 0000,也就是它自己。这会产生歧义。

为了避免这种歧义和简化硬件设计,补码标准直接定义这个模式为 -32768。因此,没有一个16位的二进制组合可以用来唯一地表示 +32768

总结表格

数值范围 二进制表示(16位) 数量
0 0000 0000 0000 0000 1个
正数 (1 到 32767) 0000 0000 0000 0001 到 0111 1111 1111 1111 32767个
负数 (-1 到 -32767) 1111 1111 1111 1111 到 1000 0000 0000 0001 32767个
负数 (-32768) 1000 0000 0000 0000 1个
总计 65536个

重要提示:关于“16位”的假设

你问题中的“-32768 到 32767”是16位有符号整数的典型范围。但需要注意的是:

  • C++标准并没有规定int类型必须是16位。它只规定了int的最小范围必须至少包含 -32767 到 32767(即至少16位)。

  • 在现代大多数系统(如Windows, Linux, macOS on x86-64架构)上,int通常是32位的,其范围是 -2,147,483,648 到 2,147,483,647。

  • 你可以通过 sizeof(int) * CHAR_BIT 来检查你当前平台上int的位数。

结论:取值范围的不对称性(-32768 到 32767 而不是到 32768)是二进制补码表示法的直接结果,这种设计使得算术运算(尤其是加法)在硬件层面变得非常简单和统一。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐