按位或运算的实现区别
1 简介
在 Go 里按位或是 |,|| 是逻辑或(只用于 bool)。本文都以 按位或 | 为例说明
2 按位或的过程(Go 示例)
规则(逐位):
0|0=0, 0|1=1, 1|0=1, 1|1=1
package main
import "fmt"
func main() {
a := 6 // 0110
b := 11 // 1011
r := a | b
fmt.Printf("%d | %d = %d\n", a, b, r)
fmt.Printf("a: %04b\n", a)
fmt.Printf("b: %04b\n", b)
fmt.Printf("r: %04b\n", r)
}
输出(概念上):
6 | 11 = 15
a: 0110
b: 1011
r: 1111
逐位过程:
0110
| 1011
= 1111 (十进制 15)
-
与 Python 的同例对比
a = 6 b = 11 r = a | b print(a, "|", b, "=", r) print(f"a: {a:04b}") print(f"b: {b:04b}") print(f"r: {r:04b}")
输出相同:
6 | 11 = 15
a: 0110
b: 1011
r: 1111
结论:按位或的语义在两种语言里是一样的:逐位取“有 1 则 1”。
3 实战的位掩码 (Go)
设定标志位(典型用法:权限/状态位):
package main
import "fmt"
const (
Read = 1 << iota // 0001
Write // 0010
Exec // 0100
)
func main() {
var perm uint8 = 0
perm |= Read | Exec // 置位
fmt.Printf("perm: %03b\n", perm) // 101
hasWrite := (perm & Write) != 0
fmt.Println("has write?", hasWrite) // false
}
Python 同理:
Read, Write, Exec = 1<<0, 1<<1, 1<<2
perm = 0
perm |= Read | Exec
print(f"perm: {perm:03b}") # 101
print("has write?", bool(perm & Write)) # False
固定位宽 vs 任意精度的直观差别
A) 超大位的或运算
Python:
x = 3 | (1 << 100) # OK,得到一个 101 位整数
Go:
x := 3 | (1 << 100) // 编译期报错:常量溢出(默认要落到 int)
// 需要:
// 1) 用 math/big
// 2) 或者把常量限制在目标类型位宽内
使用 math/big:
package main
import (
"fmt"
"math/big"
)
func main() {
a := big.NewInt(3)
b := new(big.Int).Lsh(big.NewInt(1), 100) // 1<<100
r := new(big.Int).Or(a, b)
fmt.Println(r) // 大整数结果
}
B) 负数参与位运算的语义
Python(无限二补码):
(-1) | 0 # -1
(-5) | 3 # 依无限前导 1 解释;结果仍是负数
如需“按 N 位宽解释”,常用屏蔽:((x | y) & ((1<<N)-1))
Go(定宽二补码):
var a int8 = -5 // 11111011
var b int8 = 3 // 00000011
fmt.Printf("%08b\n", a|b) // 11111011 => 仍是 -5(在 int8 里)
两者数值可能一致,但解释方式不同:Python 是“无限位”,Go 是“定 N 位”。
4 按位运算的不同
-
按位与的不同
特性 Go Python 整数类型 固定宽度(int8, int16, int32, int64, uint 等),默认 int 依赖机器字长(32/64 位)。 int 是任意精度整数(bigint),没有固定字长限制。 补码表示 所有有符号整数采用 补码表示,位数固定,因此高位会被截断或填充。 Python 的 int 在内部用一个符号位 + 动态数组表示(类 bigint),逻辑上可以看作“无限补码”,不会溢出。 按位运算结果 结果会限制在类型宽度范围内(例如 int32 会截断高于 32 位的部分)。 结果可以比原数的二进制位长大,Python 会自动扩展位数。 性能 直接映射到底层 CPU 指令,运行非常快。 Python 需要在任意精度大整数上执行逐“字块”运算,相对慢。 应用场景 常用于底层开发、位掩码、硬件寄存器操作。 常用于数学计算或大整数算法(例如加密算法中的掩码)。
-
按位或 |的不同
关键差异:算法实现与数值模型
维度 Go Python
整数模型 固定宽度(int8/int16/int32/int64/uint*,int 依平台 32/64 位)。有符号整数是二补码。 int 是任意精度大整数(bigint),位数按需增长。
按位或实现 基本映射为 CPU 的单条/少量指令,在一个机器字上做 OR(对大于字宽的类型由编译器分解为多字操作,但内置整型通常一个字)。 在 C 层面的 大整数实现:把整数拆成“字数组”,逐“字”做 OR,必要时扩容。
溢出/截断 结果被限制在类型位宽内。超出位宽的高位被截断(对 ` ` 本身不产生进位,但参与表达式的其它操作/常量转换可能报溢出或截断)。
负数参与位运算 以固定宽度二补码解释:例如 int8(-5) 的位模式是 11111011。 采用“无限位二补码语义”:负数视为无限前导 1 的扩展。
类型约束 不同整型不能直接混用,需显式转换;` 不能用于bool`。
大整数 需用 math/big:big.Int 的 Or(x, y) 方法。 内建 int 就是大整数。
性能 原生整数位运算非常快、接近硬件。 大整数按“字数组”逐块运算,开销更大;小整数仍很快,但不及原生编译型直达指令。
5 布尔与集合等的“|”差异
Go:
按位或: | 只用于整数位运算;布尔用 ||(逻辑或),且不能把 bool 当整数位运算。
Python:| 可用于
bool:True | False 得到 True(布尔是 int 子类);True | 4 == 5
set:集合并集;{1,2}|{2,3} == {1,2,3}
dict(3.9+):字典并集(键冲突后者覆盖)
这体现了 操作符重载 与 强类型/无重载 的语言设计差异。
- 运算符优先级与类型转换
优先级:两边基本一致——移位 << >> 高于按位与 &,高于按位异或 ^,高于按位或 |,逻辑或 ||/or 更低。
例:1 | 2 << 1 在两语言里都是 1 | 4 = 5。
类型转换(Go):不同位宽/有无符号不能直接“|”,需显式转换,否则编译错误;Python 不区分这些整型。
6 小结
语义层面:按位或在 Go 和 Python 都是“逐位有 1 则 1”。
差异主要在数值模型与实现:Go 的定宽二补码+接近硬件 → 快且可控;Python 的任意精度 → 更灵活但实现为“多字数组”算法。
处理超大数/密码学等:Python 直接用内建 int;Go 请使用 math/big((*Int).Or)。
处理固定位协议/寄存器:Go 的定宽类型(uint8/16/32/64)+ 位掩码最稳;
Python 场景下记得按位宽 & ((1<<N)-1) 进行屏蔽以模拟硬件位宽。
Go 的按位或是 |,逻辑或是 ||;
Python 的逻辑或是 or,|| 不合法。
- 点赞
- 收藏
- 关注作者
评论(0)