为什么Golang处理负数时会进行位翻转操作?
为什么负数的二进制内存存储不是仅翻转符号位?
我想查看数字在内存中的原始存储形式,用Go的unsafe指针做验证,我的机器是小端序(最低有效字节在最低地址,打印时排在首位)。
编写的Go代码如下:
package main import ( "fmt" "unsafe" ) func main() { var x1 int32 = 1240 ptr1 := unsafe.Pointer(&x1) value1 := (*[4]byte)(ptr1) fmt.Printf("x1 = 1240 in binary representation: %08b\n", value1) var x2 int32 = -1240 ptr2 := unsafe.Pointer(&x2) value2 := (*[4]byte)(ptr2) fmt.Printf("x2 = -1240 in binary representation: %08b\n", value2) }
执行后输出:
x1 = 1240 in binary representation: &[11011000 00000100 00000000 00000000] x2 = -1240 in binary representation: &[00101000 11111011 11111111 11111111]
我预期-1240的输出是:
x2 = -1240 in binary representation: &[11011000 00000100 00000000 10000000]
也就是仅符号位从0变1,但实际输出是所有位翻转的情况,这是为什么?
这是因为计算机系统中整数的存储和运算使用的是补码,而非你预期的原码。
原码是最直观的表示方式:正数符号位为0,负数符号位为1,其余位是数值的二进制。但原码做加减法时需要单独处理符号位,效率低,所以现代计算机都用补码来统一正负数的运算逻辑。
补码的规则:
- 正数的补码与原码完全相同
- 负数的补码 = 对应正数的原码按位取反(得到反码)后加1
结合你的例子计算:
先看1240的int32原码(小端序存储,字节顺序是低到高):
1240的二进制是00000000 00000000 00000100 11011000,小端序存储后字节顺序为11011000 00000100 00000000 00000000,和你代码输出的x1一致。计算-1240的补码:
- 先对1240的原码按位取反(反码):
11111111 11111111 11111011 00100111 - 然后加1得到补码:
11111111 11111111 11111011 00101000 - 按小端序存储,字节顺序反转成
00101000 11111011 11111111 11111111,这和你代码输出的x2完全一致。
- 先对1240的原码按位取反(反码):
你预期的是原码的存储方式,但实际上Go(和几乎所有现代编程语言)都遵循补码的存储规则,所以会出现这样的结果。
内容的提问来源于stack exchange,提问作者LeaBit
相关产品推荐
相关产品推荐

