算法通关村第11关【青铜】| 位运算基础

1.数字在计算机中的表示

原码、反码和补码都是计算机中用于表示有符号整数的方式。它们的使用旨在解决计算机硬件中的溢出和算术运算问题。

  1. 原码(Sign-Magnitude): 原码最简单,它的表示方式是用最高位表示符号位,0表示正数,1表示负数,其余位表示数值的绝对值。例如,+5 的原码是 00000101,-5 的原码是 10000101。

  2. 反码(One's Complement): 反码表示方式在原码的基础上,负数的表示方式稍作修改。对于正数,反码与原码相同。对于负数,反码是将原码中的1变成0,0变成1。例如,+5 的反码仍然是 00000101,-5 的反码是 11111010。

  3. 补码(Two's Complement): 补码是计算机中最常用的表示方式。它也在原码的基础上稍作修改。对于正数,补码与原码相同。对于负数,补码是将原码中的1变成0,然后在最低位加1。例如,+5 的补码仍然是 00000101,-5 的补码是 11111011。

为什么需要反码和补码以及它们的优点

  • 解决0的表示问题: 在原码中,+0 和 -0 有不同的表示,这可能引起混淆。而在反码和补码中,+0 和 -0 都有相同的表示。

  • 简化加法运算: 使用补码可以简化加法运算,因为负数的补码加法可以与正数的加法一样进行,而不需要额外的规则。

  • 解决溢出问题: 补码在进行加法和减法时,能够自然地处理溢出。当结果超出表示范围时,溢出的位会被丢弃,而不会引发错误。

计算机是如何进行加减运算的

  • 加法运算: 在计算机中,加法运算通常使用补码来执行。两个数相加,首先将它们的补码相加,然后检查是否有溢出。如果有溢出,溢出的位将被丢弃。补码的加法可以简化为模运算,其中模是2的n次方,n 是数据类型的位数。结果可能会包含一个溢出标志位,用于指示运算是否溢出。

  • 减法运算: 减法运算也使用补码来执行。将被减数的补码与减数的补码相加,然后检查是否有溢出。同样,结果可能会包含一个溢出标志位。这种方式使加法和减法可以使用相同的硬件电路来执行,从而简化了计算机的设计。

原码、反码和补码是计算机中表示有符号整数的方式,它们在解决符号、0表示和溢出问题方面各具优势。补码通常是最常用的表示方式,因为它能够自然地处理加法和减法,并且不需要特殊的规则。

2.位运算规则

位运算是计算机中用于操作二进制位的一组操作符。这些操作符允许您直接操纵数字的二进制表示,通常用于执行一些特定的位级操作。以下是常见的位运算操作和规则:

(1)与、或、异或、取反

  1. 按位与(AND): 使用 & 操作符执行按位与操作。将两个数字的每个位都与对应位置的位进行比较,如果都为1,则结果位为1,否则为0。

    5 & 3 = 1
    // 5 的二进制表示:101
    // 3 的二进制表示:011
    // 按位与的结果:001
    
  2. 按位或(OR): 使用 | 操作符执行按位或操作。将两个数字的每个位都与对应位置的位进行比较,如果至少有一个为1,则结果位为1,否则为0。

    5 | 3 = 7
    // 5 的二进制表示:101
    // 3 的二进制表示:011
    // 按位或的结果:111
    
  3. 按位异或(XOR): 使用 ^ 操作符执行按位异或操作。将两个数字的每个位都与对应位置的位进行比较,如果两个位不同,则结果位为1,否则为0。

    5 ^ 3 = 6
    // 5 的二进制表示:101
    // 3 的二进制表示:011
    // 按位异或的结果:110
    
  4. 按位取反(NOT): 使用 ~ 操作符执行按位取反操作。将数字的每个位都翻转,0 变为1,1 变为0。

    ~5 = -6  // 注意:取反后可能是负数
    // 5 的二进制表示:101
    // 按位取反的结果:-110
    
  5. 左移(<<)和右移(>>): 使用 << 操作符执行左移操作,将数字的二进制表示向左移动指定的位数,右侧用0填充。使用 >> 操作符执行右移操作,将数字的二进制表示向右移动指定的位数,左侧用符号位(对于有符号数)或0填充。

    5 << 2 = 20
    // 5 的二进制表示:00000101
    // 左移2位的结果:00010100
    
    -5 >> 1 = -3
    // -5 的二进制表示:11111011
    // 右移1位的结果:11111101
    

(2)算术移位和逻辑移位

算术移位是一种将数字的二进制位向左或向右移动的操作,但与逻辑移位不同,它在向右移动时保留了符号位。对于正数,算术右移将在左侧填充0,而对于负数,算术右移将在左侧填充1,以保持负数的符号。

  • 算术右移:右侧填充0或1,取决于数字的符号。
  • 算术左移:左侧填充0。

例子:

正数 5 的二进制表示: 0000 0101

正数 5 的算术右移 1 位: 0000 0010(保留符号位0,右侧填充0)

负数 -5 的二进制表示: 1111 1011

负数 -5 的算术右移 1 位: 1111 1101(保留符号位1,右侧填充1)

逻辑移位(Logical Shift)

逻辑移位是一种将数字的二进制位向左或向右移动的操作,不考虑符号位。逻辑移位在向右或向左移动时都填充0。

  • 逻辑右移:右侧填充0。
  • 逻辑左移:左侧填充0。

例子:

正数 5 的二进制表示: 0000 0101

正数 5 的逻辑右移 1 位: 0000 0010

负数 -5 的二进制表示: 1111 1011

负数 -5 的逻辑右移 1 位: 0111 1101

总结:

  • 算术移位保留符号位并根据符号位进行填充,因此用于数学运算,如除法和乘法。
  • 逻辑移位不考虑符号位,始终填充0,通常用于位级操作和数据处理。

(3)位运算常用技巧

  1. 获取特定位的值:

    num = 42   # 二进制:101010
    bit_2 = (num >> 2) & 1  # 获取第2位的值
    print(bit_2)  # 输出 1
    
    获取第n位的值:使用右移和按位与操作 ((num >> n) & 1)。这将把第n位的值提取出来(0或1)。
  2. 设置特定位的值:

    设置第n位为1:使用按位或操作 num |= (1 << n)
  3. 清零特定位:

    清零第n位:使用按位与和取反操作 num &= ~(1 << n)
  4. 更新特定位的值:

    更新第n位的值为1或0:首先清零第n位,然后使用按位或操作设置新值。

这些技巧可以用于对二进制数据进行高效的位级操作,例如对寄存器或标志位进行处理,或者用于位图操作等。在实际编程中,它们通常用于优化性能、节省内存或执行特定的位级任务。

你可能感兴趣的:(算法,算法)