Main Content

本页的翻译已过时。点击此处可查看最新英文版本。

按位运算

本主题说明如何在 MATLAB® 中使用按位运算来操作数字的位。大多数现代处理器直接支持位运算。在许多情况下,以这种方式操作数字的位比执行除法或乘法等算术运算更快。

数值表示

任何数值都可以用位来表示(也称为二进制)。数值的二进制(即基数为 2)形式包含 1 和 0,以此表示数值中都存在 2 的哪些次幂。例如,7 的 8 位二进制形式是

00000111

8 个位的集合也称为 1 个字节。在二进制表示中,位从右向左计数,因此该表示中的第一个位是 1。此数值表示 7,因为

22+21+20=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 的补码位模式,请执行以下操作:

  1. 首先找到该数值对应的正数 8 的位模式:00001000

  2. 接下来,翻转所有位:11110111

  3. 最后,对结果加 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

casttypecast 函数也可用于不同数据类型之间的切换。这些函数是相似的,但它们在如何处理数值的底层存储方面有所不同:

  • cast - 更改变量的基础数据类型。

  • typecast - 转换数据类型而不更改基础位。

由于 MATLAB 不直接显示二进制数的位,您在进行按位运算时必须注意数据类型。有些函数以字符向量形式返回二进制数字 (dec2bin),有些函数返回十进制数 (bitand),还有一些函数返回由位本身组成的向量 (bitget)。

用逻辑运算符进行位掩码

使用 MATLAB 中的一些函数,您可以对以等长二进制表示的两个数值中的位执行逻辑运算,此种运算称为位掩码

  • bitand - 如果两个位均为 1,则结果位也是 1。否则,结果位为 0。

  • bitor - 如果任一位是 1,则结果位也是 1。否则,结果位为 0。

  • bitxor - 如果位不同,则结果位为 1。否则,结果位为 0。

除了这些函数之外,您还可以使用 bitcmp 进行按位补码,但这是一元运算,一次只能翻转一个数值中的位。

位掩码的一个用途是查询特定位的状态。例如,如果对二进制数 00001000 进行按位 AND 运算,您可以查询第四个位的状态。然后,您可以将该位移至第一个位置,以便 MATLAB 返回 0 或 1(下一节将更详细地说明位移)。

n = 0b10111001;
n4 = bitand(n,0b1000);
n4 = bitshift(n4,-3)
n4 = uint8
    1

按位运算有时可以发挥意想不到的作用。例如,以下是数值 n=8 的 8 位二进制表示:

00001000

8 是 2 的幂,因此它的二进制表示只包含一个 1。现在考虑数值 (n-1)=7

00000111

由于减去了 1,从最右边的 1 开始的所有位都会翻转。因此,当 n 是 2 的幂时,n(n-1) 的对应位始终不同,使得按位 AND 返回零。

n = 0b1000;
bitand(n,n-1)
ans = uint8
    0

但是,如果 n 不是 2 的幂,则最右边的 1 表示 20 位,因此 n(n-1) 除了 20 位之外,其他位都相同。在这种情况下,按位 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 位。这等效于将 A2N 相乘

  • bitshift(A,-N)A 的位向移动 N 位。这等效于将 A 除以 2N

上述操作有时写作 A<<N(左移)和 A>>N(右移),但 MATLAB 没有将 <<>> 运算符用于此目的。

当数值的位发生移动时,数值会从末尾丢弃一些位,并引入 01 来填充新腾出的空间。当您向左移动位时,右端发生位填充;当您向右移动位时,左端发生位填充。

例如,如果将数值 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 的输入必须为相同的整数数据类型,您可以使用 0b11u160011 指定为无符号 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

另请参阅

| | | | | |

相关主题