程序如何表示负数

大一刚学 C 语言时我们就知道,表示整数的 int 类型可以有负数值。但 C 语言是如何表示负数的呢?下面我就以 char 类型为例来说明一下。

无符号数的表示很简单,即把二进制位直接视为二进制数。char 类型的值可以取的范围是 [0, 255] 共 256 个数。

Bin Hex Dec
00000000b $00 0
00000001b $01 1
11111111b $FF 255

对于有符号数,首先一个比较容易想到的方案就是,让出最高位作为符号位,剩下的 7 位表示数字的绝对值。比如 $85 表示 -5,$FF 表示 -127。映射关系如下。

Bin Hex Dec
00000000b $00 0
00000001b $01 1
01111111b $7F 127
10000000b $80 -0
10000001b $81 -1
11111111b $FF -127

这个方案可以表示数的区间为 [-127, 127],共 255 个数,比无符号数少了一个,即 $80 表示了毫无意义的 -0。更大的问题是,负数的增长方向与对应的无符号数方向相反,这会给计算带来麻烦。

另一种方案就是把区间整个移动到 [-127, 128] 上,这样就可以表示 256 个数。实际上这即是浮点数表示中指数的表示方法。这种方法便于计算,并且有最高位作为符号位,在类型转换时,需要对数值进行操作,最高位取反。

Bin Hex Dec
00000000b $00 -127
00000001b $01 -126
01111111b $7F -1
10000000b $80 0
10000001b $81 1
11111111b $FF 127

实际上,计算机中负数的表示方法叫做 two’s-complement,大约是上面两种情况的结合。让表示无符号数的右半个部分的位码来表示有符号数的负数。这种方法有最高位来作为符号位,便于计算,并且便于类型转换。实际上,位数相同的整数类型间进行类型转换时,数据本身没有任何变化。

Bin Hex Dec
00000000b $00 0
00000001b $01 1
01111111b $7F 127
10000000b $80 -128
10000001b $81 -127
11111111b $FF -1

理解了这一点,也就明白了其他长度整数的表示。比如对于任意位的无符号整数,全 1 都表示 -1,最小值为 1 后全 0,最大值为 0 后全 1。对于整数的计算,无符号数与有符号数之间几乎没有差异。