1. 无符号数、原码、反码、补码

计算机中的数值需要按一定的方式进行存储:

  1. 原码
  2. 反码
  3. 补码

对于正数来说,原码和反码和补码都是一样的,对于负数有不同的规则。
从记忆的角度来看,无符号数是最基本的二进制表示,有符号数是将最高位作为符号位,剩余位数的计数和无符号数保持一致。
我们将有符号数称为原码。反码和补码就无法直接肉眼得出数值。
反码 = 原码的符号位不变,剩余取反(或者是这个数的正数部分所有位取反)
补码 = 反码 + 1

比如我们要求 -127 这个数的补码: 首先知道 127 的二进制为 0111 1111 取反得到 1000 0000 再加 1 得到 1000 0001 再顺一下 -127 这个数字的各个码的表达形式: 无符号数 = 无法表示 有符号数(原码) = (1)111 1111 符号位为1 反码 = 1000 0000 补码 = 1000 0001

有助于理解的表格:
Python 位运算 - 图1
网图,侵删

2.意义

补码是为了简化cpu运算,使得加减乘除运算仅通过加法器+补码表示就能实现。
如果直接使用原码进行运算,直接用加法运算原码算不出正确的结果,而引入复杂逻辑判断符号位会复杂化 cpu 的设计

1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2

因此出现了补码,使得有符号数能直接通过加法器实现减法运算,乘法和除法也就可以实现了。

1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0

3. Python 位运算

3.1 位运算

  1. print(f"11 = {bin(11)}")
  2. # 取反
  3. print(f"~11 = {bin(~11)}")
  4. # 左移
  5. print(f"11 << 2 = {bin(11 << 2)}")
  6. # 右移
  7. print(f"11 >> 2 = {bin(11 >> 2)}")
  8. print(f"10 = {bin(10)}")
  9. # 按位与
  10. print(f"10&11 = {bin(10&11)}")
  11. # 按位或
  12. print(f"10|11 = {bin(10|11)}")
  13. # 按位异或
  14. print(f"10^11 = {bin(10^11)}")
  15. 11 = 0b1011
  16. ~11 = -0b1100
  17. 11 << 2 = 0b101100
  18. 11 >> 2 = 0b10
  19. 10 = 0b1010
  20. 10&11 = 0b1010
  21. 10|11 = 0b1011
  22. 10^11 = 0b1

3.2 利用位运算实现快速运算

2的倍数
  1. n = 3
  2. # n 乘 2 的 1 次方
  3. print(f"n = {n}")
  4. print(f"n*2 = {n << 1}")
  5. print(f"n*(2^n) = {n << n}")
  6. print(f"2^n = {1 << n}")
  7. n = 3
  8. n*2 = 6
  9. n*(2^n) = 24
  10. 2^n = 8

快速交换2个数
  1. a = 2
  2. b = 3
  3. a ^= b
  4. b ^= a
  5. a ^= b
  6. print(f"a = {a}")
  7. print(f"b = {b}")
  8. a = 3
  9. b = 2

快速获取 a 最后一个位置的数
  1. a = 10
  2. print(f"a = {bin(a)}")
  3. print(f"a & (-a) = {bin(a & (-a))}")
  4. a = 0b1010
  5. a & (-a) = 0b10
  6. a = 0b1011
  7. a & (-a) = 0b1

3.3 利用位运算实现整数集合

todo

参考

https://www.cxybb.com/article/qq_25474469/79614508