按位非在不同语言的实现差异

举报
码乐 发表于 2025/09/02 08:59:01 2025/09/02
【摘要】 1 简介Go 中按位非(^x)的过程,按位非把二进制表示的数字每一位翻转:0→1,1→0,并且在该类型的位宽内进行。无符号示例(uint8) package main import "fmt" func main() { var a uint8 = 0b01011010 // 0x5A = 90 r := ^a ...

1 简介

Go 中按位非(^x)的过程,按位非把二进制表示的数字每一位翻转:0→1,1→0,并且在该类型的位宽内进行。

			a  : 01011010
			^a  : 10100101   => 0xA5 = 165

无符号示例(uint8)

    package main

    import "fmt"

    func main() {
        var a uint8 = 0b01011010 // 0x5A = 90
        r := ^a                  // 在 8 位内翻转
        fmt.Printf("a    = %08b (%d)\n", a, a) // 01011010 (90)
        fmt.Printf("^a   = %08b (%d)\n", r, r) // 10100101 (165)
    }

逐位过程(仅 8 位):

      a  : 01011010
     ^a  : 10100101   => 0xA5 = 165

有符号示例(int8)

    package main

    import "fmt"

    func main() {
        var b int8 = 6 // 二补码 00000110
        c := ^b        // 在 8 位内翻转 → 11111001,对应 int8 的 -7
        // 想看“原始比特”,把有符号转成无符号再打印
        fmt.Printf("b   bits: %08b (val=%d)\n", uint8(b), b) // 00000110 (6)
        fmt.Printf("^b  bits: %08b (val=%d)\n", uint8(c), c) // 11111001 (-7)
    }

要点: Go 的有符号整数用定宽二补码。在 N 位内,^x == -x-1 (mod 2^N)。

2 Python 中的对应操作

Python 的按位非是 ~x,且 int 是任意精度大整数。

~x 的数学恒等式:

	~x == -x-1(无限二补码语义)。

因为是“无限位”,不自动截断到 8/32/64 位。

			a = 0b01011010  # 90
			print(~a)       # -91  (不是 165!因为没有按 8 位截断)

如果你想模拟“在 8 位内取反”(等价 Go 的 uint8 效果),要加屏蔽:

    a = 0b01011010      # 90
    r8 = (~a) & 0xFF    # 截到 8 位
    print(r8)           # 165
    print(f"{r8:08b}")  # 10100101

同理,要看 int8 的比特模式,用 & 0xFF 再格式化:

  b = 6
  c = ~b              # -7
  print(c)            # -7
  print(f"{c & 0xFF:08b}")  # 11111001  (与 Go 的 int8 结果比特一致)

3 对比差异:数值模型与实现

    维度			Go									Python
    运算符	一元 ^x = 按位非;二元 a ^ b = 异或		~x = 按位非;a ^ b = 异或
    整数模型	定宽(int8/16/32/64/uint*;int 依平台 32/64 位),二补码	 			任意精度大整数(位数按需增长),逻辑上“无限二补码”
    取反结果	在类型位宽内翻转,高位受位宽限制;^x == -x-1 (mod 2^N)			无限位语义,直接得到数学值 -x-1,不会自动截到 N 位
    查看比特	负数打印比特需转无符号(如 uint8(x))			负数格式化会带负号;要看 N 位比特需 & ((1<<N)-1)
    性能/实现	贴近硬件:对原生整型通常就是单条 CPU 指令			在大整数“字数组”(limbs)上逐块取反,再规范化符号;灵活但开销更高
    大数支持	超过原生位宽需 math/big,用 (*Int).Not/组合			内建 int 就是大数

小贴士:

清位(bit clear): Go 有专门的 &^(AND NOT):x &^ mask。Python 用 x & ~mask。

跨语言移植: 需要明确位宽(8/16/32/64…),在 Python 侧用 & ((1<<N)-1) 进行屏蔽。

  • 常见用法对照

A) 清除若干标志位

  const (
      R = 1 << 0
      W = 1 << 1
      X = 1 << 2
  )
  var perm uint8 = R | W | X     // 0b111
  perm = perm &^ (W | X)         // 清除 W、X
  // perm == R

Python:

    R, W, X = 1<<0, 1<<1, 1<<2
    perm = R | W | X               # 0b111
    perm = perm & ~(W | X)         # 清除 W、X
    
		perm == R

B) 有符号取反恒等式验证

Go(以 int32 为例)

var x int32 = 123
y := ^x
z := -x - 1
fmt.Println(y == z) // true

Python

  x = 123
  print(~x == -x - 1)  # True

4 注意事项

Python 中 ^ 不是取反:从Go迁移到python 的用户 ^ 直接带到 Python,用成按位非,结果得到的是“异或”。

位宽差异:Go 是定宽,Python 是无限位。要在 Python 中复现 Go 的结果,务必加屏蔽:

  def notN(x, N):        # 在 N 位内取反
      return (~x) & ((1<<N)-1)

打印比特表示:Go 的负数打印需要转无符号;Python 的负数需要 & ((1<<N)-1) 后再格式化。

布尔与集合:Go 的 ^ 不能用于 bool 或集合;Python 的 ^ 在 bool 上是异或、在 set 上是对称差,~ 只对整数有效。

常量溢出(Go):^ 作用于未类型化常量时可“无限精度”,一旦赋给定宽类型会按目标类型截断或在编译期报溢出。

4 小结

语义:按位非在两语言都是“逐位翻转”;

符号:Go 用一元 ^x,Python 用 ~x;

核心差异:Go 在固定位宽内翻转(贴近硬件),Python 采用无限二补码语义(得到 -x-1,不自动截断)。

实务:跨语言时明确位宽;Python 侧用掩码还原 Go 的定宽效果;清位在 Go 用 &^,在 Python 用 & ~

【版权声明】本文为华为云社区用户翻译文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容, 举报邮箱:cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。