按位运算
本主题说明如何在 MATLAB® 中使用按位运算来操作数字的位。大多数现代处理器直接支持位运算。在许多情况下,以这种方式操作数字的位比执行除法或乘法等算术运算更快。
数值表示
任何数值都可以用位来表示(也称为二进制位)。数值的二进制(即基数为 2)形式包含 1 和 0,以此表示数值中都存在 2 的哪些次幂。例如,7 的 8 位二进制形式是
00000111
8 个位的集合也称为 1 个字节。在二进制表示中,位从右向左计数,因此该表示中的第一个位是 1。此数值表示 7,因为
当您在 MATLAB 中键入数值时,它假设数值为双精度(64 位二进制表示)。但是,您也可以指定单精度数(32 位二进制表示)和整数(有符号或无符号,从 8 到 64 位)。例如,要存储数值 7,最节省内存的方法是使用 8 位无符号整数:
a = uint8(7)
a = uint8
7
您甚至可以直接使用前缀 0b
后跟二进制数字来指定二进制形式(有关详细信息,请参阅十六进制和二进制值)。MATLAB 以位数最少的整数格式存储数值。您只需指定最左边的 1 和它右边的所有位,而不必指定所有位。该位左边的位是无效零。因此数值 7 是:
b = 0b111
b = uint8
7
MATLAB 使用 2 的补码存储负整数。以 8 位有符号整数 -8 为例。要找到该数值的 2 的补码位模式,请执行以下操作:
首先找到该数值对应的正数 8 的位模式:
00001000
。接下来,翻转所有位:
11110111
。最后,对结果加 1:
11111000
。
得到的 11111000
是 -8 的位模式:
n = 0b11111000s8
n = int8
-8
MATLAB 并不主动显示数值的二进制格式。为此,您可以使用 dec2bin
函数,该函数会返回正整数的二进制数字字符向量。同样,此函数只返回不包含无效零的位。
dec2bin(b)
ans = '111'
您可以使用 bin2dec
在这两种格式之间切换。例如,您可以使用以下命令将二进制数字 10110101
转换为十进制格式
data = [1 0 1 1 0 1 0 1]; dec = bin2dec(num2str(data))
dec = 181
cast
和 typecast
函数也可用于不同数据类型之间的切换。这些函数是相似的,但它们在如何处理数值的底层存储方面有所不同:
由于 MATLAB 不直接显示二进制数的位,您在进行按位运算时必须注意数据类型。有些函数以字符向量形式返回二进制数字 (dec2bin
),有些函数返回十进制数 (bitand
),还有一些函数返回由位本身组成的向量 (bitget
)。
用逻辑运算符进行位掩码
使用 MATLAB 中的一些函数,您可以对以等长二进制表示的两个数值中的位执行逻辑运算,此种运算称为位掩码:
除了这些函数之外,您还可以使用 bitcmp
进行按位补码,但这是一元运算,一次只能翻转一个数值中的位。
位掩码的一个用途是查询特定位的状态。例如,如果对二进制数 00001000
进行按位 AND 运算,您可以查询第四个位的状态。然后,您可以将该位移至第一个位置,以便 MATLAB 返回 0 或 1(下一节将更详细地说明位移)。
n = 0b10111001; n4 = bitand(n,0b1000); n4 = bitshift(n4,-3)
n4 = uint8
1
按位运算有时可以发挥意想不到的作用。例如,以下是数值 的 8 位二进制表示:
00001000
8 是 2 的幂,因此它的二进制表示只包含一个 1。现在考虑数值 :
00000111
由于减去了 1,从最右边的 1 开始的所有位都会翻转。因此,当 是 2 的幂时, 和 的对应位始终不同,使得按位 AND 返回零。
n = 0b1000; bitand(n,n-1)
ans = uint8
0
但是,如果 不是 2 的幂,则最右边的 1 表示 位,因此 和 除了 位之外,其他位都相同。在这种情况下,按位 AND 返回一个非零数值。
n = 0b101; bitand(n,n-1)
ans = uint8
4
受上述运算启发,我们可以编写一个简单的函数对给定的输入数值执行位运算,以判断它是否为 2 的幂:
function tf = isPowerOfTwo(n) tf = n && ~bitand(n,n-1); end
使用短路 AND 运算符 &&
检查以确保 n
不为零。如果为零,则该函数不需要计算 bitand(n,n-1)
即可知道正确答案是 false
。
移位
由于按位逻辑运算比较两个数值中对应的位,因此,能够按需移位以便于比较对应位就显得非常重要。您可以使用 bitshift
执行此操作:
bitshift(A,N)
将A
的位向左移动N
位。这等效于将A
和 相乘。bitshift(A,-N)
将A
的位向右移动N
位。这等效于将A
除以 。
上述操作有时写作 A<<N
(左移)和 A>>N
(右移),但 MATLAB 没有将 <<
和 >>
运算符用于此目的。
当数值的位发生移动时,数值会从末尾丢弃一些位,并引入 0
或 1
来填充新腾出的空间。当您向左移动位时,右端发生位填充;当您向右移动位时,左端发生位填充。
例如,如果将数值 8(二进制:1000
)右移一位,则得到 4(二进制:100
)。
n = 0b1000; bitshift(n,-1)
ans = uint8
4
同样,如果将数值 15(二进制:1111
)左移两位,则得到 60(二进制:111100
)。
n = 0b1111; bitshift(15,2)
ans = 60
当您移动负数的位时,bitshift
会保留有符号位。例如,如果将有符号整数 -1(二进制:11111101
)向右移动 2 位,则得到 -1(二进制:11111111
)。在这些情况下,bitshift
在左端填充 1
而不是 0
。
n = 0b11111101s8; bitshift(n,-2)
ans = int8
-1
写入位
您可以使用 bitset
函数来更改数值中的位。例如,将数值 8 的第一个位更改为 1(相当于将该数值加 1):
bitset(8,1)
ans = 9
默认情况下,bitset
将位翻转为 on 或 1。您可以选择使用第三个输入参数来指定位值。
bitset
不会一次更改多个位;要更改多个位,您需要使用 for
循环。因此,您更改的位可以是连续的,也可以是非连续的。例如,更改二进制数 1000
的前两位:
bits = [1 2]; c = 0b1000; for k = 1:numel(bits) c = bitset(c,bits(k)); end dec2bin(c)
ans = '1011'
bitset
的另一个常见用途是将二进制数字向量转换为十进制格式。例如,使用循环来设置整数 11001101
的各个位。
data = [1 1 0 0 1 1 0 1]; n = length(data); dec = 0b0u8; for k = 1:n dec = bitset(dec,n+1-k,data(k)); end dec
dec = uint8
205
dec2bin(dec)
ans = '11001101'
读取连续位
位移的另一个用途是隔离位的连续部分。例如,读取 16 位数值 0110000010100000
中的最后四位。前面提到,最后四位位于二进制表示的左端。
n = 0b0110000010100000; dec2bin(bitshift(n,-12))
ans = '110'
要隔离该数值中间的连续位,可以结合使用位移和逻辑掩码。例如,要提取第 13 位和第 14 位,可以向右移动 12 位,然后用 0011
对所得的 4 位进行掩码。由于 bitand
的输入必须为相同的整数数据类型,您可以使用 0b11u16
将 0011
指定为无符号 16 位整数。如果没有 -u16
后缀,MATLAB 会将数值存储为无符号 8 位整数。
m = 0b11u16; dec2bin(bitand(bitshift(n,-12),m))
ans = '10'
读取连续位的另一种方法是使用 bitget
,它从数值中读取指定的位。您可以使用冒号表示法指定要读取的几个连续位。例如,读取 n
的最后 8 位。
bitget(n,16:-1:8)
ans = 1x9 uint16 row vector
0 1 1 0 0 0 0 0 1
读取非连续位
您也可以使用 bitget
从数值中读取彼此不相邻的位。例如,从 n
中读取第 5、8 和 14 位。
bits = [14 8 5]; bitget(n,bits)
ans = 1x3 uint16 row vector
1 1 0
另请参阅
bitand
| bitor
| bitxor
| bitget
| bitset
| bitshift
| bitcmp