补码的深入理解

本文最后更新于:3 years ago

之前我有写过关于原码补码和反码的文章,

原码反码和补码

但是都是大概介绍了一下如何定义以及如何计算,并没有深究。

这段时间在看《深入理解计算机系统》时,正好看到关于补码的介绍,介绍得非常得详细。这里想记录关于补码的一些深度理解。

问题引入

在刚学C语言时,学习到关于数据类型Int时,我们会发现书上给的Int类型的取值范围是在
-2 147 483 648 ~ 2 147 483 647
你有没有思考过这么一个问题,为什么取值的正负值不是对称的呢?我们会发现|Min|=|Max|+1,有人可能会考虑0,但是,0在其中起到什么作用呢?

其实,这就和我们接下来要说的补码有关系了。(本想从数学上来更深入讨论补码的原理,但确实很多符号打不出来,就放弃从原理层面来介绍了。)

补码编码

我们知道,在计算机中,分为有符号数和无符号数,C语言默认是有符号数,用unsigned来表示无符号数(Java只支持有符号数)。
其中最常见的有符号数的计算机表示方式就是补码(two’s-complement)形式。在计算机系统中,数值一律用补码来表示和存储。补码可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。

我们知道,其实在计算机中的所以的一切,都是由二进制组成的。
在程序中,我们给变量赋的值,不论多大的数,在计算机底层其实都是由0和1组成的。
比如,short类型的12345的十六进制是0x3039,转化为二进制就是 [0011 0000 0011 1001]

而short类型的-12345转化为二进制是 [1011 0000 0011 1001]

我们可以发现,12345-12345的二进制,其实就是最高位一个是0,一个是1。

在计算机中,我们将最高有效位称之为符号位,0即代表这个数为正数,而1,代表这个数为负数。

所以对于补码,符号位为0时,数的取值范围为
0 ~ 2 147 483 647

而符号位为1时,数的取值范围为
-2 147 483 648 ~ 0

正数的补码就是其原码,而负数的原码则是其反码+1。
所以12345在计算机存储中就是按照原码来存储的,其补码等于原码。而我们所看到的-12345,它的原码是 [1011 0000 0011 1001] ,但在存储中是按照补码来的,也就是说存储在计算机中其实是 [1100 1111 1100 0111] 转化为十进制是53191

原因

结合以上知识,之所以会有这样的不对称性,是因为一半的位模式(符号位设置为1的数)表示负数,而另一半是(符号位设置为0的数)表示非负数。因为0是非负数,也就意味着能表示的整数比负数少一个。

总结

其实并不是很深入地理解,本想从数学角度来讨论补码的,但确实很多数学符号不好表示,所以就比较简单地介绍了一下补码。
如果真的想要了解数学原理,可以去看看《深入理解计算机系统》这本书,里面介绍得真的很详细。