工作中常用到的二进制运算

工作中常用到的二进制运算

进制转换表

二进制 八进制 十进制 十六进制
0000 0 0 0
0001 1 1 1
0010 2 2 2
0011 3 3 3
0100 4 4 4
0101 5 5 5
0110 6 6 6
0111 7 7 7
1000 10 8 8
1001 11 9 9
1010 12 10 A
1011 13 11 B
1100 14 12 C
1101 15 13 D
1110 16 14 E
1111 17 15 F

Go语言中的进制表示

在Go语言中,可以使用不同前缀表示不同进制的数:

go 复制代码
package main

import "fmt"

func main() {
    // 二进制表示 (0b 或 0B 开头)
    binaryNum := 0b1010     // 十进制 10
    binaryNum2 := 0B1100    // 十进制 12
    
    // 八进制表示 (0o, 0O 或 0 开头)
    octalNum := 0o12        // 十进制 10
    octalNum2 := 0O16       // 十进制 14
    octalNum3 := 012        // 传统写法,十进制 10
    
    // 十六进制表示 (0x 或 0X 开头)
    hexNum := 0xA           // 十进制 10
    hexNum2 := 0xFF         // 十进制 255
    
    fmt.Printf("二进制 0b1010 = %d\n", binaryNum)
    fmt.Printf("八进制 0o12 = %d\n", octalNum)
    fmt.Printf("十六进制 0xA = %d\n", hexNum)
}

二进制运算基础

1. 位运算(Bitwise Operations)

与运算(&)​​:两个位都为1时,结果才为1

go 复制代码
func andOperation() {
    a := 0b1010  // 10
    b := 0b1100  // 12
    result := a & b  // 0b1000 = 8
    
    fmt.Printf("%b & %b = %b (十进制: %d)\n", a, b, result, result)
    // 输出: 1010 & 1100 = 1000 (十进制: 8)
}

或运算(|)​​:两个位有一个为1时,结果就为1

go 复制代码
func orOperation() {
    a := 0b1010  // 10
    b := 0b1100  // 12
    result := a | b  // 0b1110 = 14
    
    fmt.Printf("%b | %b = %b (十进制: %d)\n", a, b, result, result)
    // 输出: 1010 | 1100 = 1110 (十进制: 14)
}

异或运算(^)​​:两个位不同时,结果为1

go 复制代码
func xorOperation() {
    a := 0b1010  // 10
    b := 0b1100  // 12
    result := a ^ b  // 0b0110 = 6
    
    fmt.Printf("%b ^ %b = %b (十进制: %d)\n", a, b, result, result)
    // 输出: 1010 ^ 1100 = 110 (十进制: 6)
}

非运算(^ 或 ^=)​​:在Go中,一元^是按位取反

go 复制代码
func notOperation() {
    a := 0b1010  // 10
    result := ^a  // 在64位系统中: -11
    
    // 要只取低4位,可以使用掩码
    maskedResult := result & 0b1111  // 0b0101 = 5
    
    fmt.Printf("^%b = %b (完整: %064b)\n", a, maskedResult, result)
}

清除位运算(&^)​​:Go语言特有的位清空运算符

go 复制代码
func andNotOperation() {
    a := 0b1011  // 11
    b := 0b1100  // 12
    result := a &^ b  // 0b0011 = 3
    
    fmt.Printf("%b &^ %b = %b (十进制: %d)\n", a, b, result, result)
    // 输出: 1011 &^ 1100 = 11 (十进制: 3)
    // 解释: 在b为1的位置,将a的对应位清零
}

2. 移位运算(Shift Operations)

左移(<<)​​:向左移动指定位数,低位补0

go 复制代码
func leftShift() {
    a := 0b1010  // 10
    result := a << 2  // 0b101000 = 40
    
    fmt.Printf("%b << 2 = %b (十进制: %d)\n", a, result, result)
    // 输出: 1010 << 2 = 101000 (十进制: 40)
}

右移(>>)​​:向右移动指定位数

go 复制代码
func rightShift() {
    a := 0b1010  // 10
    result := a >> 2  // 0b0010 = 2
    
    fmt.Printf("%b >> 2 = %b (十进制: %d)\n", a, result, result)
    // 输出: 1010 >> 2 = 10 (十进制: 2)
}

实际应用场景

1. 权限控制

使用位掩码进行权限管理:

go 复制代码
package main

import "fmt"

const (
    Read   = 1 << iota  // 0b0001 = 1
    Write               // 0b0010 = 2
    Execute             // 0b0100 = 4
    Admin               // 0b1000 = 8
)

type User struct {
    name       string
    permissions int
}

func (u *User) addPermission(perm int) {
    u.permissions |= perm
}

func (u *User) removePermission(perm int) {
    u.permissions &^= perm
}

func (u *User) hasPermission(perm int) bool {
    return (u.permissions & perm) != 0
}

func main() {
    user := User{name: "Alice", permissions: 0}
    
    // 添加权限
    user.addPermission(Read | Write)
    
    // 检查权限
    fmt.Printf("%s 有读取权限: %v\n", user.name, user.hasPermission(Read))
    fmt.Printf("%s 有执行权限: %v\n", user.name, user.hasPermission(Execute))
    
    // 权限二进制表示
    fmt.Printf("权限位: %04b\n", user.permissions)
}

2. 标志位处理

go 复制代码
package main

import "fmt"

// 使用 iota 自动生成位标志
const (
    FlagA = 1 << iota  // 0b0001
    FlagB              // 0b0010
    FlagC              // 0b0100
    FlagD              // 0b1000
)

func processFlags(flags int) {
    if flags & FlagA != 0 {
        fmt.Println("FlagA 被设置")
    }
    
    if flags & FlagB != 0 {
        fmt.Println("FlagB 被设置")
    }
    
    // 检查多个标志
    if flags & (FlagA | FlagB) == (FlagA | FlagB) {
        fmt.Println("同时设置了FlagA和FlagB")
    }
}

func main() {
    flags := FlagA | FlagC
    fmt.Printf("当前标志: %04b\n", flags)
    processFlags(flags)
}

3. 快速乘除法运算

go 复制代码
func fastMath() {
    num := 0b1010  // 10
    
    // 快速乘以2^n
    multiplyBy4 := num << 2  // 10 * 4 = 40
    multiplyBy8 := num << 3  // 10 * 8 = 80
    
    // 快速除以2^n
    divideBy2 := num >> 1    // 10 / 2 = 5
    divideBy4 := num >> 2    // 10 / 4 = 2
    
    fmt.Printf("10 * 4 = %d\n", multiplyBy4)
    fmt.Printf("10 / 2 = %d\n", divideBy2)
}

4. 奇偶判断

go 复制代码
func isEvenOdd() {
    numbers := []int{0b1010, 0b1011, 0b1100, 0b1101}
    
    for _, n := range numbers {
        // 与 0b1 进行与运算判断奇偶
        if n&1 == 0 {
            fmt.Printf("%d (%b) 是偶数\n", n, n)
        } else {
            fmt.Printf("%d (%b) 是奇数\n", n, n)
        }
    }
}

进阶技巧

1. 获取最低位的1

dart 复制代码
func getLowestOneBit() {
    num := 0b101100  // 十进制 44
    
    // 获取最低位的1
    // 原理: -num 是 num 的二进制补码
    // 例如: num = 44 (0b101100)
    // -num = -44 (二进制补码: ...1111010100)
    // num & -num 会得到只保留最低位1的结果
    lowestBit := num & -num
    
    fmt.Printf("数字: %d (%b)\n", num, num)
    fmt.Printf("最低位的1: %d (%b)\n", lowestBit, lowestBit)
    // 输出: 数字: 44 (101100)
    //       最低位的1: 4 (100)
}

2. 判断是否为2的幂

go 复制代码
func isPowerOfTwo() {
    numbers := []int{8, 9, 16, 32, 33}  // 二进制: 1000, 1001, 10000, 100000, 100001
    
    for _, n := range numbers {
        // 2的幂的特征: 二进制表示中只有一个1
        // 原理: 如果n是2的幂,那么n-1的所有低位都是1
        // 例如: 8(1000) & 7(0111) = 0
        if n > 0 && (n & (n-1)) == 0 {
            fmt.Printf("%d (%b) 是2的幂\n", n, n)
        } else {
            fmt.Printf("%d (%b) 不是2的幂\n", n, n)
        }
    }
}

3. 统计1的个数(位计数)

go 复制代码
func countOnes() {
    num := 0b101101  // 45,二进制有4个1
    
    // 方法1: 循环计数
    count := 0
    temp := num
    for temp != 0 {
        count++
        // 关键技巧: n & (n-1) 会清除最低位的1
        // 每次循环清除一个1,直到所有1都被清除
        temp &= temp - 1
    }
    
    fmt.Printf("数字 %d (%b) 有 %d 个1\n", num, num, count)
    
    // 方法2: 使用内置函数(Go 1.9+)
    // import "math/bits"
    // count = bits.OnesCount(uint(num))
}

4. 反转二进制位

go 复制代码
func reverseBits() {
    num := 0b10100111  // 167
    reversed := 0
    bits := 8  // 假设处理8位
    
    for i := 0; i < bits; i++ {
        // 1. 将结果左移1位,为最低位腾出空间
        reversed <<= 1
        
        // 2. 获取原数的最低位
        // 3. 将原数的最低位放到结果的最低位
        reversed |= (num & 1)
        
        // 4. 原数右移1位,处理下一位
        num >>= 1
    }
    
    fmt.Printf("原始: %08b\n", 0b10100111)
    fmt.Printf("反转: %08b\n", reversed)
    // 输出: 原始: 10100111
    //       反转: 11100101
}

5. 提取连续位

go 复制代码
func extractBits() {
    num := 0b11011010  // 218
    
    // 提取第3-5位(从右向左,从0开始计数)
    start := 3
    end := 5
    
    // 原理:
    // 1. 创建掩码: (1 << (end-start+1)) - 1
    //    例如提取3位: (1 << 3) - 1 = 0b111
    // 2. 右移start位,将目标位移动到最低位
    // 3. 与掩码进行与运算,只保留需要的位
    mask := (1 << (end - start + 1)) - 1
    result := (num >> start) & mask
    
    fmt.Printf("原始数字: %08b\n", num)
    fmt.Printf("提取第%d-%d位: %03b (十进制: %d)\n", start, end, result, result)
    // 输出: 提取第3-5位: 101 (十进制: 5)
}

6. 检查特定位是否被设置

go 复制代码
func checkBit() {
    num := 0b10110110  // 182
    
    // 检查第4位(从0开始计数)
    pos := 4
    
    // 方法1: 右移后检查最低位
    bit1 := (num >> pos) & 1
    
    // 方法2: 创建掩码后检查
    mask := 1 << pos
    bit2 := (num & mask) != 0
    
    fmt.Printf("数字: %08b\n", num)
    fmt.Printf("第%d位: %d (方法1)\n", pos, bit1)
    fmt.Printf("第%d位: %v (方法2)\n", pos, bit2)
}

实用工具函数

go 复制代码
package main

import (
    "fmt"
    "strings"
)

// 格式化显示二进制
func formatBinary(num int, bits int) string {
    return fmt.Sprintf("%0*b", bits, num)
}

// 设置特定位
func setBit(num int, pos int) int {
    return num | (1 << pos)
}

// 清除特定位
func clearBit(num int, pos int) int {
    return num &^ (1 << pos)
}

// 切换特定位(0变1,1变0)
func toggleBit(num int, pos int) int {
    return num ^ (1 << pos)
}

// 检查特定位是否被设置
func isBitSet(num int, pos int) bool {
    return (num & (1 << pos)) != 0
}

// 获取最低位1的位置
func getLowestBitPosition(num int) int {
    if num == 0 {
        return -1
    }
    position := 0
    for (num & 1) == 0 {
        num >>= 1
        position++
    }
    return position
}

func main() {
    num := 0b11011010
    
    fmt.Printf("原始数字: %s\n", formatBinary(num, 8))
    fmt.Printf("设置第2位: %s\n", formatBinary(setBit(num, 2), 8))
    fmt.Printf("清除第3位: %s\n", formatBinary(clearBit(num, 3), 8))
    fmt.Printf("切换第5位: %s\n", formatBinary(toggleBit(num, 5), 8))
    fmt.Printf("第4位是否设置: %v\n", isBitSet(num, 4))
    fmt.Printf("最低位1的位置: %d\n", getLowestBitPosition(num))
}

性能优势

Go语言中的位运算在性能上有显著优势:

  1. 编译时优化:Go编译器能识别位运算模式并进行优化
  2. CPU原生支持:现代CPU都有专门的位运算指令
  3. 内存高效:多个布尔标志可以打包到一个整型中
css 复制代码
// 性能对比示例
package main

import (
    "fmt"
    "time"
)

func main() {
    const iterations = 1000000000
    
    // 使用乘法
    start := time.Now()
    for i := 0; i < iterations; i++ {
        _ = i * 2
    }
    multiplyTime := time.Since(start)
    
    // 使用位移
    start = time.Now()
    for i := 0; i < iterations; i++ {
        _ = i << 1
    }
    shiftTime := time.Since(start)
    
    fmt.Printf("乘法用时: %v\n", multiplyTime)
    fmt.Printf("位移用时: %v\n", shiftTime)
    fmt.Printf("位移比乘法快: %.2f%%\n", 
        float64(multiplyTime.Nanoseconds()-shiftTime.Nanoseconds())/float64(multiplyTime.Nanoseconds())*100)
}

总结

在Go开发中,二进制运算不仅是底层编程的基础,也是编写高效代码的重要工具。通过合理使用:

  1. 权限和标志位管理:使用位掩码提高存储效率
  2. 数学优化:位移替代乘除提升性能
  3. 算法实现:在算法竞赛和性能敏感场景中广泛应用
  4. 位操作技巧:解决特定问题的高效方案

掌握这些技巧能让你的Go代码更加高效、优雅。建议在实际项目中多尝试使用位运算,特别是在处理标志、权限、状态等场景时。

相关推荐
转转技术团队2 小时前
转转大数据与AI——数据治理安全打标实践
大数据·人工智能·后端
利刃大大2 小时前
【SpringBoot】SpringMVC && 请求注解详解 && 响应注解详解 && Lombok
java·spring boot·后端
梨子同志2 小时前
Java 介绍与开发环境安装
后端
她说..2 小时前
Spring AOP场景4——事务管理(源码分析)
java·数据库·spring boot·后端·sql·spring·springboot
用户4099322502122 小时前
Vue3动态样式控制:ref、reactive、watch与computed的应用场景与区别是什么?
后端·ai编程·trae
心之语歌2 小时前
3433.统计用户被提及情况
后端
爬山算法3 小时前
Netty(17)Netty如何处理大量的并发连接?
java·后端
李慕婉学姐3 小时前
Springboot面向电商的仓库管理系统05uc4267(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端