程序如何表示负数
大一刚学 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。对于整数的计算,无符号数与有符号数之间几乎没有差异。