NumPy位运算教程:bitwise_and按位与与移位取反
位运算直接操作二进制数的每一位,不依赖数字的十进制整体值。NumPy 把这类操作封装成矢量化函数,能直接作用于整个数组,底层由 C 实现,因此在处理大规模数据时通常比 Python 原生循环逐元素做位运算更高效。NumPy 位运算函数均以 bitwise_ 开头,同时也支持与 Python 原生位运算符对应的简写形式。一、NumPy 位运算函数与运算符总览
按位与:numpy.bitwise_and(x1, x2),对应运算符 &,两数对应位均为 1 时结果为 1,否则为 0。
按位或:numpy.bitwise_or(x1, x2),对应运算符 |,两数对应位有一个为 1 时结果为 1,否则为 0。
按位异或:numpy.bitwise_xor(x1, x2),对应运算符 ^,两数对应位不同时结果为 1,相同时为 0。
按位取反:numpy.invert(x),对应运算符 ~,对数字每一位取反;有符号数需要结合补码规则理解。
左移:numpy.left_shift(x1, x2),对应运算符 <<,将 x1 的二进制位向左移动 x2 位,右侧补 0。
右移:numpy.right_shift(x1, x2),对应运算符 >>,将 x1 的二进制位向右移动 x2 位,左侧根据符号位补 0 或 1,正数补 0。
二、按位与 bitwise_and 与 & 的用法
按位与的规则是:1 & 1 = 1,1 & 0 = 0,0 & 1 = 0,0 & 0 = 0。
import numpy as np
# 单个整数运算
a, b = 13, 17
print(f"13 的二进制:{bin(a)},17 的二进制:{bin(b)}")# 0b1101 0b10001
print("按位与结果:", np.bitwise_and(a, b))# 等价于 13 & 17,输出 1
# 数组运算(矢量化)
arr1 = np.array(, dtype=int)
arr2 = np.array(, dtype=int)
print("数组按位与:", arr1 & arr2)# 输出
13 的二进制为 01101,17 的二进制为 10001。按位对齐后逐位计算,结果为 00001,即十进制的 1。数组运算中, 与 逐元素按位与,得到 ,这也是 NumPy 矢量化能力的直接体现。
三、按位或 bitwise_or 与 | 的用法
按位或的规则是:1 | 1 = 1,1 | 0 = 1,0 | 1 = 1,0 | 0 = 0。
import numpy as np
a, b = 13, 17
print(f"13 的二进制:{bin(a)},17 的二进制:{bin(b)}")# 0b1101 0b10001
print("按位或结果:", np.bitwise_or(a, b))# 等价于 13 | 17,输出 29
13 | 17 的计算过程为:01101 与 10001 逐位或,得到 11101,即十进制 29。
四、按位异或 bitwise_xor 与 ^ 的用法
按位异或的规则是:两数对应位不同时结果为 1,相同时为 0。它同样适用于布尔数组,在布尔值上等价于逻辑异或,True 视为 1,False 视为 0。
import numpy as np
arr1 = np.array(, dtype=bool)
arr2 = np.array(, dtype=bool)
# 函数与运算符等价
result1 = np.bitwise_xor(arr1, arr2)
result2 = arr1 ^ arr2
print("异或结果:", result1)# 输出 [ True True True]
print("结果一致:", np.array_equal(result1, result2))# 输出 True
上面两个布尔数组逐元素异或后,结果均为 True,并且函数调用与运算符写法的结果完全一致。
五、按位取反 invert 与 ~ 的用法
按位取反对数字的每一位执行 0 变 1、1 变 0。需要注意的是,结果受数组 dtype 影响:无符号整数直接逐位取反;有符号整数则要按二进制补码规则解释,最高位为符号位,0 表示正,1 表示负。
无符号整数取反示例:
import numpy as np
# uint8 类型(0-255,8位二进制)
x = np.array(, dtype=np.uint8)
x_invert = np.invert(x)
print(f"13 的 8 位二进制:{np.binary_repr(13, width=8)}")# 00001101
print(f"取反后的值:{x_invert}")# 242
print(f"242 的 8 位二进制:{np.binary_repr(242, width=8)}")# 11110010
uint8 的 13 表示为 00001101,逐位取反后为 11110010,即十进制的 242。
有符号整数取反示例:
import numpy as np
x = 5# 默认 int 类型(32位)
x_invert = ~x
print("5 的二进制(简化):00000101")
print("~5 的二进制(补码):11111010")
print(f"~5 的十进制:{x_invert}")# 输出 -6
以 ~1 为例说明补码计算步骤:1 的 8 位有符号原码为 00000001;逐位取反后为 11111110,此时符号位为 1,代表负数;再按补码规则处理,除符号位外取反加 1:10000001 + 1 = 10000010;转换为十进制即 -2。
六、左移 left_shift 与 << 的用法
左移将数字的二进制位向左移动指定位数,右侧用 0 填充,等价于乘以 2 的 n 次方,n 为移位数。
import numpy as np
x = 10# 二进制:00001010
shifted = np.left_shift(x, 2)# 等价于 10 << 2
print(f"10 左移 2 位结果:{shifted}")# 输出 40
print(f"40 的二进制:{np.binary_repr(40, width=8)}")# 00101000
10 的二进制为 00001010,左移 2 位后变为 00101000,即 40,对应数学运算 10 × 2² = 40。
七、右移 right_shift 与 >> 的用法
右移将数字的二进制位向右移动指定位数,左侧根据符号位补 0 或 1,正数补 0,等价于整除 2 的 n 次方。
import numpy as np
x = 40# 二进制:00101000
shifted = np.right_shift(x, 2)# 等价于 40 >> 2
print(f"40 右移 2 位结果:{shifted}")# 输出 10
print(f"10 的二进制:{np.binary_repr(10, width=8)}")# 00001010
40 的二进制为 00101000,右移 2 位后变为 00001010,即 10,对应数学运算 40 ÷ 2² = 10。
八、运算符使用注意事项
优先级问题:位运算符的优先级低于算术运算符,计算时建议加括号。例如 10 & 1 + 2 会先算 1 + 2 得到 3,再算 10 & 3,结果为 2;如果写成 (10 & 1) + 2,求值顺序不同,虽然该例结果碰巧也是 2,但不应依赖这种巧合。
数组兼容性:参与运算的数组需要满足 NumPy 的广播规则,即形状一致或可以广播。
数据类型影响:取反运算的结果受数组 dtype 影响,无符号类型与有符号类型的表现不同,实际使用时建议明确指定类型,避免歧义。
九、典型应用场景
底层数据处理:操作二进制文件中的字节数据,如图片、音频等。
状态标志位控制:用单个整数的不同位表示多个状态,例如用 0b101 表示 3 个状态的组合。
性能优化:用左移、右移替代乘法和除法,在部分场景下速度更快。
加密与校验:基于异或等运算实现简单的数据加密或校验,如 CRC 校验。
十、总结
NumPy 位运算围绕按位与、按位或、按位异或、按位取反、左移和右移展开,函数形式与运算符形式基本一一对应。实际编码时重点注意三点:位运算优先级低于算术运算,数组需要满足广播规则,取反结果受 dtype 影响。掌握这些细节后,就可以把位运算用于数组级二进制处理、状态标志管理和性能优化等场景。
Re: NumPy位运算教程:bitwise_and按位与与移位取反
楼主的整理挺系统的,先总览函数和运算符,再逐个讲按位与、或、异或、取反和左移,思路很清楚。按位与那段的 13 和 17 二进制对齐计算,以及数组 和 得到 的例子,把矢量化优势讲得很直观;异或用在布尔数组上的示例也说明了函数和运算符写法等价。取反部分把 uint8 的 242 和有符号数补码的 -6、-2 放在一起对比,容易理解 dtype 对结果的影响。不过帖子最后左移的 print 好像被截断了,右移也只在总览里带过,如果能把左移示例补完整,再补一段右移和正负数补位区别,这篇教程就更完整了。感谢分享。Re: NumPy位运算教程:bitwise_and按位与与移位取反
这篇整理得挺系统的,函数名和运算符对应关系一目了然,按位与、或、异或、取反、左移的示例也都很直观。特别是 invert 那部分提醒了 dtype 不同结果会不一样,无符号整数直接取反,有符号整数要按补码理解,这点很容易被忽略,例子挺有帮助。 不过最后左移那段好像没贴完,print 后面断了,右移也还没展开。期待把右移和移位时符号位、补位这些细节补上,这样整篇就更完整了。Re: NumPy位运算教程:bitwise_and按位与与移位取反
感谢分享,整理得很系统。函数名和 &、|、^、~、 的对应关系列得很清楚,数组矢量化那部分也把 NumPy 的优势讲出来了。按位取反这段尤其有用,uint8 的 13 取反得到 242,而有符号的 ~5 得到 -6,能让人直观感受到 dtype 和补码的重要性。布尔数组异或的例子也很实用。左移等价于乘 2 的 n 次方好记,不过实际用的时候要注意数据类型位宽,移位太多可能会溢出或截断。第六节左移部分好像还没贴完,期待把右移和更多组合用法继续补充完整。
页:
[1]