1 前言
我的上一篇文章谈论到在汇编语言中
为什么 SHL 和 SAL 相同,而 SHR 和 SAR 不同
由此产生的疑惑更多了
到底什么是符号位? 进而补码又是什么?
2 为什么要有补码?
理由很简单,计算机内部只有加法器,并没有减法器
所以在计算机中,在我们人类眼中很简单的算术运算 2 - 3 = -1
计算机是无法完成的,那么依照计算机只能做加法,很显然的想到了 2 + (-3) = -1
因此数在计算机中必然要有特定的表示,那么补码就诞生了。
3 什么是补码?
无符号(unsigned)的一个字节的数表示如下:
0000 0000 = 0
0000 0001 = 1
0000 0010 = 2
...
1111 1111 = 255
有符号数(signed)的一个字节的数表示如下:
0000 0000 = 0
0000 0001 = 1
0000 0010 = 2
...
1000 0000 = 0
1000 0001 = -1
...
1111 1111 = -127
问题出现了,0000 0000
和 1000 0000
都代表着数字
0
0
0
不仅如此,且计算两个数相加会非常复杂,比如:
0000 0010
+ 1000 1100
---------------
???? ????
这必然给计算机运算器带来麻烦
于是约定,将1000 0000
设置为 -128
,了解更多 点这里
因此范围被定义为:
−
128
-128
−128
≤
\leq
≤ X
≤
\leq
≤
+
127
+127
+127
虽然两个 0 的表示问题解决了,但是计算问题依旧相当复杂,于是就引进了补码
几乎所有的教材都这样写到:
正数的补码就是原码
负数的补码是符号位不变(1),其余位取反,然后加一并且给出以下定义:
一个有符号数的最高位为符号位
0 代表这个数为正数
1 代表这个数为负数
依照这种定义,我们简单的来进行几个运算:
【例1】用补码计算 45 + 24
。
0010 1101
+ 0001 1000
---------------
0100 0101
正常答案69
也是轻而易举的求出来了
【例2】用补码计算45 - 38
。
0010 1101
+ 1101 1010
-----------------
(1) 0000 0111
计算这一题的时候就有些犹豫了,因为进位已经到了符号位
那么我的符号位到底参与运算吗?
常理来说,符号位顾名思义,代表着数的正负,怎么可以参与运算呢?
事实却恰恰相反,符号位参与运算的结果才是正确的,如上例所示。
4 什么才是正真的补码?
上述的例子说明了一个非常矛盾的事情
把一个数的最高位定义为了符号位,计算中符号位却能参与运算
想必这也是很多人蒙在鼓里的地方了
补码的定义(以 8 8 8位字长数为例):
表示 | 范围 |
---|---|
X X X | 0 0 0 ≤ \leq ≤ X X X ≤ \leq ≤ + 127 +127 +127 |
2 8 2^8 28- X X X | − 128 -128 −128 ≤ \leq ≤ X X X < 0 <0 <0 |
也就是说,正数的补码就是原码
而反码的补码等于位数
2
8
2^8
28 减去
X
X
X 的绝对值
∣
X
∣
|X|
∣X∣,例如
−
38
-38
−38 的补码:
1 0000 0000
- 0010 0110
----------------
1101 1010
所以
−
38
-38
−38 的补码为1101 1010
在这种定义下,没有反码的概念,也没有符号位的概念
参考:
[1] Signed Int: Bias/Excess Notation