位运算

位运算

左移和右移: 乘除运算…

简单理解,左移和右移就是指二进制位向左和向右移动,左移n位即乘以$$2^n$$ ,同样右移n位即除以2n2^n

1
2
3
2 << 1 = 4  // 左移 1  0000 0010 --> 0000 0100
2 << 2 = 8 // 左移 2 0000 0010 --> 0000 1000
2 << 3 = 16 // 左移 3 0000 0010 --> 0001 0000

右移逻辑分析同样,但是有一点需要注意,当移位超出进制位数范围时,则为0.

1
2
3
2 >> 1 = 1 // 右移 1   0000 0010 --> 0000 0001
2 >> 2 = 0 // 右移 2 0000 0010 --> 0000 0000 //超出范围了
2 >> 3 = 0 // 右移 3 0000 0010 --> 0000 0000 //超出范围了 ...

位运算中的左移(<<)和右移(>>)操作在编程中有多种应用场景,尤其是在处理底层数据、优化性能和特定算法时。以下是它们的常用应用场景:

左移(<<)的应用场景

  1. 快速乘法
    左移操作相当于将数字乘以2的n次方。例如,a << n 相当于 a * 2^n。在某些场景下,使用位移操作可以比乘法运算更高效。

    • 示例x << 3 相当于 x * 8
  2. 数据打包
    在嵌入式系统或通信协议中,有时需要将多个小数据段打包到一个整型变量中。通过左移操作,可以将各个数据段放置在不同的位段上。

    • 示例:将两个4位的数字打包成一个8位的字节。
  3. 位标志的设置
    左移可以用来设置特定位的标志。例如,1 << n 可以生成一个在第n位上的掩码(mask),常用于控制位标志的设置。

    • 示例flags |= (1 << 5) 设置第5位的标志。

右移(>>)的应用场景

  1. 快速除法
    右移操作相当于将数字除以2的n次方(向下取整)。a >> n 相当于 a // 2^n。这在某些场景下比除法运算更高效。

    • 示例x >> 2 相当于 x // 4
  2. 提取位段
    右移操作常用于从整数中提取特定的位段。通过先右移后取与操作(&),可以得到某些位的值。

    • 示例:提取一个32位整数中的第4到第7位。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      # 假设有一个32位整数 num,我们想要提取第4到第7位的值:
      num = 0b10111011010100011110001001011100 # 示例整数

      # 步骤1:右移4位,将第4到第7位移动到最低的4个位
      shifted = num >> 4

      # 步骤2:使用掩码 0b1111(即十六进制的 0xF)来提取最低的4位
      extracted_bits = shifted & 0b1111 # 或者 & 0xF

      # 以二进制格式打印结果
      print(f"提取的第4到第7位:{bin(extracted_bits)}") ##0b101

  3. 除法结果的快速判定
    对于某些特定的应用场景,通过右移操作可以快速判断一个数字是否可以被2的幂次整除,并获取其商的整数部分。

  4. 符号扩展
    在符号位的右移操作中,通常使用算术右移,以保留符号位。它在处理带符号整数时特别有用,比如对负数进行二进制操作时。

这些位运算操作在需要高效数据处理、控制硬件、图像处理、加密算法等场景中尤为常见。


按位与& : 相同为1,否则为0

规则: 相同位置上均为1,则返回1;否则为0;

这个按位与的操作,非常频繁,务必掌握,下面我们来看看其应用场景

按位与(&)操作是一种非常常用的位运算操作,通常用于数据处理、算法优化以及系统编程中。以下是按位与操作的常用应用场景:

1. 掩码操作

按位与操作最常见的用途是掩码操作,即从一个数值中提取特定位的值或清除特定位。通过与特定的掩码(mask)进行按位与,可以保留所需的位,并将其他位清零。

  • 提取特定位的值:假设你有一个8位的二进制数 11010110,你想要提取其中的中间4位(第2到第5位),可以用 & 0b00111100
  • 示例代码
    1
    2
    3
    num = 0b11010110
    mask = 0b00111100
    result = num & mask # 提取中间4位,结果为0b00010100

2. 位标志(Flags)操作

在系统编程中,按位与常用于处理位标志。例如,一个字节的每一位可能代表一个不同的状态或标志,通过按位与可以检查某个标志是否被设置。

  • 检查特定位是否为1:如果你想检查第2位(bit = 2)是否为1,可以用 num & (1 << 2)

  • 示例代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def test2():
    flags = 0b10101010
    check = flags & (1 << 2) # 检查第2位是否为1
    if check != 0:
    print("第2位为1")
    else:
    print("第2位为0")

    test2() ## 第2位为0

3. 清除特定位

按位与可以用于清除特定位置的值(将其置为0),而不影响其他位。

  • 清除特定位:如果你想将一个数的第3位清零,可以用 num & ~(1 << 3)。(~表示取反的操作符)

  • 示例代码

    1
    2
    3
    num = 0b11111111  # 初始值为全1
    num &= ~(1 << 3) # 清除第3位
    print(bin(num)) # 结果为 0b11110111

4. 位字段(Bit Fields)

在某些情况下,按位与用于管理和操作位字段(bit fields),例如在嵌入式系统中,一个整数的不同位段可能代表不同的数据或配置选项。

  • 示例:将多个配置项打包成一个整数,并通过按位与操作从中提取特定的配置项。

5. 奇偶性检测

按位与操作可以用于快速判断一个数的奇偶性。通过 num & 1,可以检测一个数的最低位,从而判断其是奇数还是偶数。

  • 示例代码
    1
    2
    3
    4
    5
    num = 10
    if num & 1 == 0:
    print("偶数")
    else:
    print("奇数")

按位或|:只要有一个为1,则返回1

规则:只要对应的两位其中一位是 1 ,则返回1

&的规则不同,我们可以将其理解为

  • & == and
  • | == or

按位或(|)操作在编程中也有许多重要的应用场景,尤其是在需要设置或合并特定位的场合。以下是按位或操作的一些常用应用场景:

1. 位标志(Flags)的设置

按位或操作常用于设置特定位的值(将其置为1)而不影响其他位。这在管理多个标志或选项时特别有用。

  • 示例:假设你有一个表示不同配置选项的位标志,你可以通过按位或操作来设置某个特定的选项。
  • 示例代码
    1
    2
    3
    flags = 0b10101000  # 原始标志
    flags |= 0b00000010 # 设置bit1为1
    print(bin(flags)) # 结果为 0b10101010

2. 合并位模式

当你需要将多个位模式组合在一起时,按位或是非常有用的工具。例如,将两个或多个数字的位合并成一个新的数字。

  • 示例:合并两个8位的二进制数。
  • 示例代码
    1
    2
    3
    4
    num1 = 0b11000000
    num2 = 0b00110000
    result = num1 | num2 # 结果为 0b11110000
    print(bin(result))

3. 默认值设置

在某些情况下,如果你需要确保一个值至少有某些位是设置好的,可以使用按位或来强制设置这些位,而不改变其他已经设置好的位。

  • 示例:确保某个数的第0位和第1位至少有一个是1。
  • 示例代码
    1
    2
    3
    num = 0b11001000
    num |= 0b00000011 # 确保第0位和第1位至少有一个为1
    print(bin(num)) # 结果为 0b11001011

4. 启用特定功能

按位或操作可以用于启用某个功能或选项。在系统配置或硬件控制中,通常通过设置某个位来启用对应的功能。

  • 示例:启用某个硬件功能,通过设置特定位来打开相应的开关。
  • 示例代码
    1
    2
    3
    hardware_config = 0b00000000  # 初始配置
    hardware_config |= 0b00000100 # 启用第3位功能
    print(bin(hardware_config)) # 结果为 0b00000100

5. 初始化多位变量

在嵌入式系统或低级别编程中,按位或操作经常用于初始化多位变量,使其包含多个启用的标志位。

  • 示例:初始化一个变量以启用多个特性。
  • 示例代码
    1
    2
    3
    init_value = 0b00000000
    init_value |= 0b01010101 # 启用奇数位
    print(bin(init_value)) # 结果为 0b01010101

6. 构建位图

在图形编程中,按位或操作可以用于构建位图或图像掩码,通过逐位或操作,将不同图像元素合并成一个完整的图像。

  • 示例:将多个图像层合并成一个图像。
  • 示例代码
    1
    2
    3
    4
    layer1 = 0b10101010
    layer2 = 0b11001100
    combined_image = layer1 | layer2 # 合并图像层
    print(bin(combined_image)) # 结果为 0b11101110

7. 权限控制

在权限管理系统中,按位或操作用于添加或启用特定的权限位。例如,在文件系统或用户权限设置中,可以通过按位或来授予用户新的权限。

  • 示例:为用户添加写权限。
  • 示例代码
    1
    2
    3
    4
    permissions = 0b0001  # 初始权限,只有读权限
    write_permission = 0b0010
    permissions |= write_permission # 添加写权限
    print(bin(permissions)) # 结果为 0b0011,表示读和写权限

8. 设置字节的高位或低位

当你需要同时设置某个字节的高位或低位时,可以使用按位或来确保目标位的正确性。

  • 示例:设置字节的高位为1,保持低位不变。
  • 示例代码
    1
    2
    3
    byte = 0b00001111
    byte |= 0b11110000 # 设置高4位为1
    print(bin(byte)) # 结果为 0b11111111

这些场景展示了按位或操作在控制位状态、合并数据和初始化变量等方面的重要性。特别是在底层开发和系统编程中,按位或操作是一个不可或缺的工具。


按位异或^:相同为0,不同为1

规则:对应两位 不相同 返回1相同返回0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 假设 a = 5, b = 4 ;在不适用第三个变量的情况下,交换两个数值
int a = 5;
int b = 4;
// 5 : 0101
// 4 : 0100
// a = 5 ^ 4 = 0101 ^ 0100 = 0001 = 1
// b = 1 ^ 4 = 0001 ^ 0100 = 0101 = 5
// a = 1 ^ 5 = 0001 ^ 0101 = 0100 = 4
a = a ^ b; // 这一步其实是相当于临时变量,临时变量缓存一个中间值,保持最原始的 a ^ b
b = a ^ b; // 这一步相当于 (a ^ b) ^ b 结果是 a
a = a ^ b; // 这一步相当于 (a ^ b) ^ [(a ^ b) ^ b] == b

a = 4;
b = 5;

一个数,位异或另一个数两次,结果还是这个数。

位异或(^)操作是一种非常灵活且有趣的位运算操作,它在多种编程场景中有着广泛的应用。以下是一些常见的应用场景:

1. 位翻转

位异或操作的一个显著特性是,当一个数与1进行异或时,对应位会翻转(0变1,1变0)。这使得异或操作非常适合用于位翻转。

  • 示例:将一个字节的第3位翻转。
  • 示例代码
    1
    2
    3
    num = 0b10101100
    num ^= 0b00001000 # 翻转bit3
    print(bin(num)) # 结果为 0b10100100

2. 加密与解密

异或操作在简单加密算法中非常常见。由于相同的数据多次异或会恢复原始数据,所以它常用于对称加密和解密。

  • 示例:使用异或操作加密和解密数据。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    data = 0b11001100
    key = 0b10101010

    # 加密
    encrypted = data ^ key

    # 解密
    decrypted = encrypted ^ key

    print(bin(encrypted)) # 加密后的结果
    print(bin(decrypted)) # 解密后的结果,应该与data相同

3. 交换两个变量的值

异或操作可以用来在不使用额外临时变量的情况下交换两个变量的值。这种方法虽然不如直接交换常见,但在一些底层编程中具有一定的技巧性和趣味性。

  • 示例:交换两个整数。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    a = 5  # 0b0101
    b = 9 # 0b1001

    a = a ^ b
    b = a ^ b
    a = a ^ b

    print(a) # 输出9
    print(b) # 输出5

4. 查找数组中唯一的元素

如果一个数组中只有一个元素是独一无二的,而其他元素都是成对出现的,可以利用异或的性质快速找到这个唯一的元素。成对出现的元素异或后结果为0,最终异或结果就是那个独一无二的元素。

  • 示例:找到数组中唯一的非重复元素。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    arr = [2, 3, 5, 4, 5, 3, 4]
    unique_element = 0

    for num in arr:
    unique_element ^= num

    print(unique_element) # 结果为2,这是唯一的非重复元素

5. 数据校验

异或操作常用于简单的数据校验,如奇偶校验。通过异或所有位,可以判断数据是否发生了错误(如传输错误或存储错误)。

  • 示例:实现一个简单的奇偶校验。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    data = [1, 0, 1, 1]  # 假设这些是二进制位
    parity = 0

    for bit in data:
    parity ^= bit

    print(parity) # 如果parity为1,则表明数据中的1个数为奇数,否则为偶数

6. 图像处理

在图像处理领域,异或操作可以用于图像混合、掩码操作以及生成视觉效果,如图像对比、边缘检测等。

  • 示例:图像对比,通过异或操作高亮显示两个图像的不同之处。
  • 示例
    1
    2
    3
    4
    5
    pixel1 = 0b11001100  # 假设这是一个图像像素
    pixel2 = 0b10101010 # 另一个图像像素

    difference = pixel1 ^ pixel2 # 结果显示两个像素的不同
    print(bin(difference)) # 结果为 0b01100110

7. 生成伪随机数

虽然异或操作本身不是一个随机数生成器,但它可以用于伪随机数生成算法中,帮助混淆和打乱位模式,以增强随机性。

  • 示例:使用异或操作混淆伪随机数生成。
  • 示例
    1
    2
    3
    seed = 0b11001010
    random_value = seed ^ (seed << 1)
    print(bin(random_value))

8. 状态翻转

在状态管理中,异或操作可以用于翻转某些位,适合用于控制系统中的开关状态变化。

  • 示例:翻转开关状态。
  • 示例
    1
    2
    3
    4
    5
    state = 0b0001  # 当前状态,只有第0位为1
    toggle = 0b0001 # 我们要翻转第0位

    new_state = state ^ toggle
    print(bin(new_state)) # 结果为 0b0000,即第0位被翻转

这些应用场景展示了位异或操作的灵活性和多功能性,尤其在数据处理、算法优化、加密以及低级别编程中,它是一个非常有用的工具。


按取取反~

规则: 0变1 ;1变0

一个二进制数据经过取反(按位非)操作后,生成的是其反码

解释:

  • 原码:这是一个数在计算机中的直接二进制表示方式。正数的原码与其二进制表示相同,负数的原码是将该数的绝对值用二进制表示,然后在最高位(符号位)上设为1。

  • 反码:反码是通过对原码的每一位进行取反操作得到的,即将原码中的0变为1,1变为0。对于正数,反码与原码相同。对于负数,反码则是在原码的基础上除了符号位外,其余位进行取反。

  • 补码:补码是在反码的基础上加1得到的。补码是计算机中处理负数的标准方式。正数的补码与其原码相同,负数的补码则是其反码加1。

示例:

假设一个8位二进制数 00000101(即十进制数5):

  • 原码00000101
  • 反码11111010(对原码每一位取反)

对于负数的例子,如 -5,其二进制原码为 10000101

  • 原码10000101
  • 反码11111010(符号位不变,其他位取反)
  • 补码11111011(符号位不变,其他位取反)

所以,当一个二进制数经过取反操作后,得到的结果是它的反码。如果继续对反码加1,则得到的是补码,这在计算机中用于表示负数。

取反(也称为按位取反或按位非,使用符号 ~)操作将数字的每一位都反转,即将0变为1,将1变为0。以下是取反操作的常用应用场景:

1. 快速求相反数

在二进制补码表示法中,取反操作可以用来快速计算一个数的相反数(即负数),结合加1操作即可得到一个数的负值。

  • 示例:计算一个整数的相反数。
  • 示例代码
    1
    2
    3
    4
    num = 5  # 0b00000101
    1111 1010
    negative_num = ~num + 1 # 结果为 -5
    print(negative_num)

2. 掩码生成

在一些位操作中,取反常用于生成掩码(mask)。例如,当需要清除特定的位时,可以先对掩码取反,然后与原数据进行按位与操作。

  • 示例:清除一个整数的高4位。
  • 示例代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    num = 0b11111111  # 原始数据
    mask = 0b00001111 # 需要保留低4位
    result = num & mask # 直接与掩码相与
    print(bin(result)) # 结果为 0b00001111

    # 如果要清除高4位,可以使用取反
    mask = ~0b11110000 # 取反后为 0b00001111
    result = num & mask
    print(bin(result)) # 结果为 0b00001111

3. 快速生成全1的位模式

通过对0进行取反操作,可以快速生成一个全1的位模式。这在需要初始化掩码或进行其他位操作时非常有用。

  • 示例:生成一个8位全1的二进制数。
  • 示例代码
    1
    2
    3
    4
    all_ones = ~0  # 结果是全1的二进制数
    # 通常会按位数限制,如生成8位全1:
    all_ones &= 0xFF # 结果为 0b11111111
    print(bin(all_ones))

4. 差值计算

在某些情况下,取反操作可用于计算两个位模式之间的差异。例如,判断两个数哪些位是不同的。

  • 示例:找出两个数中不同的位。
  • 示例代码
    1
    2
    3
    4
    5
    6
    num1 = 0b10101010
    num2 = 0b11001100

    difference = num1 ^ num2 # 结果表示不同位的位置
    inverted_difference = ~difference # 取反表示相同位的位置
    print(bin(inverted_difference))

5. 状态切换

在状态管理中,取反操作常用于切换某些状态。通过对某些位取反,可以在启用和禁用状态之间进行切换。

  • 示例:切换设备的开关状态。
  • 示例代码
    1
    2
    3
    4
    5
    state = 0b10101010  # 当前状态
    toggle_mask = 0b00000001 # 要切换第0位状态

    new_state = state ^ toggle_mask # 取反特定位
    print(bin(new_state)) # 结果为 0b10101011

6. 生成补码

在二进制系统中,生成一个数的补码时,通常需要先对数进行取反,再加1。补码表示法在计算机中用于表示负数。

  • 示例:生成一个数的补码。
  • 示例代码
    1
    2
    3
    num = 5  # 0b00000101
    complement = ~num + 1 # 生成补码
    print(complement) # 结果为 -5

7. 屏蔽位

通过取反操作,可以屏蔽某些不需要的数据位,只保留需要的部分。例如,从一个二进制数中剔除高位。

  • 示例:屏蔽高位,只保留低4位。
  • 示例代码
    1
    2
    3
    4
    num = 0b11011011
    mask = 0b00001111 # 仅保留低4位
    result = num & mask
    print(bin(result)) # 结果为 0b00001011

8. 逻辑否定

在某些编程语言中,位取反操作可以用于模拟逻辑上的否定操作,特别是在处理位模式或位字段时。

  • 示例:对布尔值取反。
  • 示例代码
    1
    2
    3
    bool_val = 0b00000001  # 逻辑上为True
    inverted_bool = ~bool_val & 1 # 逻辑取反
    print(inverted_bool) # 结果为0,表示False

位运算
https://jackiedai.github.io/2024/09/03/012进制/003.位运算/
Author
lingXiao
Posted on
September 3, 2024
Licensed under