。为此,在表示负数时就需要使用“二进制的补码”。补码就是用正数来表示负数
负数
- 对于有符号正数,一般将最高位作为符号位,1表示负数,0表示正数
- 计算机在做减法运算时,实际上内部是在做加法运算。即加上一个负数
- 负数是用正数的补码表示的,而不是将正数的符号位0直接改成1,如果符号位为1,那么这个数是负数,且是用正数的补码表示的
补码
- 将二进制数的各数位的数值全部取反,然后再将结果加1
- $-A$的补码为$A$
- 可以用负数的补码来求相反数即正数
推导
$$ \begin{align} max + 1 &= 0 \ 原码 + 原码各数位取反 + 1 &= 0 \ 原码 + 补码 &= 0 \ \end{align} $$
表示范围
以int16
举例,表示范围为$[-2^{15}, 2^{15}-1]$,共$2^{16}个数$。负数为$[-2^{15}, -1]$共$2^{15}$个数,正数为$[1, 2^{15}-1]$共$2^{15}$个数
看起来负数好像表示范围比正数多1个,因为0000000000000000
表示0,而1000000000000000
则是用补码表示的负数,其相反数(可用补码即所有数位取反后加1求出来)是1000000000000000
即$2^{15}$,也就是说1000000000000000
表示$-2^{15}$。两者相加后最高位进位(更高位被舍弃)因此结果是0,这两者确实是互为相反数
位移
左移
直接将所有数位左移,在剩下的数位上补0即可
- 正数:每个数位的1都进位了1,因此所表示的值都翻倍了
- 负数: 如果符号位的右一位不是0,说明这个负数再左移(翻倍)一次就必定会超过表示范围,从而造成不可预料的结果 反之如果左移后仍在表示范围内(恰好,其相反数即正数也在表示范围内),则符号位的右一位必定也是1。此时可以换一种思路,先通过补码转成正数,再左移1位,再通过补码变回负数,至于符号位当然要保持为1不变来表示负数,而符号位的右一位1左移后刚好作为表示负数的符号位1。具体可自行验证
右移
将所有数位右移,然后在剩下的数位上补上和符号位相同的数
为什么相比左移多了判断符号位的步骤?
比如将-4右移1位变为-2,可以先通过补码(按位取反再加1)变为4,然后4右移1位变为2,再通过补码变为-2,在这些过程中,并不会改变符号(正负),因此正数要补0,负数要补1