左移(<<
)和右移(>>
)操作是位操作中的两种基本操作,它们直接在操作数的二进制表示上进行操作。这里通过具体例子来解释左移和右移的含义和效果。
左移操作(<<
)
左移操作将数字的二进制位向左移动指定的位数,右侧空出的位用0填充。对于无符号整数,左移一位相当于乘以2。例如,假设我们有一个8位的二进制数0001 0100
(十进制中的20),将它左移3位得到1 0100 000
(十进制中的160):
原始值:0001 0100 (20)
左移3位:1 0100 000 (160)
右移操作(>>
)
右移操作将数字的二进制位向右移动指定的位数。对于空出的位,无符号整数通常用0填充,而有符号整数的填充则依赖于具体的实现,通常是用符号位(最左边的位)的值填充,这被称为算术右移。例如,假设我们有一个8位的二进制数0001 0100
(十进制中的20),将它右移2位得到0000 0101
(十进制中的5):
原始值:0001 0100 (20)
右移2位:0000 0101 (5)
如果是有符号整数的右移,比如1111 1000
(-8,假设是8位有符号整数),右移3位可能得到1111 1110
(-2,假设使用算术右移):
原始值:1111 1000 (-8)
右移3位:1111 1110 (-2)
这里的关键点是:
- 左移(
<<
)操作相当于将数字乘以2
的移动位数次方。 - 右移(
>>
)操作相当于将数字除以2
的移动位数次方(对于无符号整数)或执行算术右移(对于有符号整数)。
负数表示(补码)
在计算机中,负数通常使用二进制的补码(two’s complement)表示法来存储。补码是一种使加法和减法操作更简单且对硬件友好的方式,允许使用相同的电路来处理正数和负数的加法。对于一个给定的二进制数,其补码表示法如下定义:
- 正数的补码是其自身。
- 负数的补码是其绝对值的二进制表示的逆(每个1变成0,每个0变成1,称为一的补码或反码),然后加1。
让我们用一个8位的二进制数为例来解释为什么1111 1000
表示-8
:
首先,我们找到8
的二进制表示。因为8
是一个正数,所以直接用其二进制表示:
0000 1000
要找到-8
的补码,我们先取8
的二进制表示的反码(即每位取反):
1111 0111
然后,在反码的基础上加1得到补码:
1111 0111
+ 1
---------
1111 1000
所以,1111 1000
是-8
的补码表示。
在8位有符号整数的补码表示法中,最高位(最左边的位)是符号位:0
表示正数,1
表示负数。因此,1111 1000
的最高位是1
,表示这是一个负数,其余位表示该负数的补码值,解码后就是-8
。这种表示法允许-128
到127
(对于8位整数)的整数范围,并且使得二进制的加法和减法运算适用于包括负数在内的所有整数。
补码(Two’s Complement)表示法的设计使得8位整数可以表示的范围是从-128到+127,这里是为什么:
范围解释
-
正数和零:在补码表示法中,正数和零的最高位(称为符号位)是0,其余位表示数值本身。因此,对于8位整数,最大的正数是
0111 1111
(127 in decimal),因为这是在符号位为0的情况下其余7位都为1时的最大值。 -
负数:负数使用其正数形式的补码表示。最小的负数是
1000 0000
。尽管直觉上看起来这个值应该表示-128(并且它确实如此),但其特殊之处在于这个表示没有正数形式的补码——也就是说,没有一个8位二进制数的补码是1000 0000
。这是因为尝试找到-(-128)
的补码会发现它指回自身,因此1000 0000
被用来表示-128,这是8位二进制补码表示法中可能的最小值。
二进制加法和减法
补码表示法的一个关键优点是它允许使用相同的硬件电路进行加法和减法运算,包括负数的情况,而无需对正数和负数进行不同的处理。这是如何实现的:
-
加法:两个补码表示的数相加时,如果最终结果超出了表示范围,会发生溢出,且只保留结果的最低8位(对于8位整数)。例如,
0111 1111
(127)加0000 0001
(1)会导致溢出,结果是1000 0000
(-128),这符合预期的数学结果,尽管在二进制层面发生了溢出。 -
减法:减法可以通过对被减数加上减数的补码(即负数表示)来实现。这意味着,如果你想计算
A - B
,你可以将其转换为A + (-B)
的加法操作。这里,-B
是通过取B
的补码来获取的。补码的计算方法是先取反(每位取反),然后加1。