为什么这个Bug如此狡猾?
直觉背叛:我们直觉上认为 -A 就是 “负的A”,但在位运算中它变成了”A的补码”
测试遗漏:我们测试了正数情况,但对负数的测试不够充分
认知偏差:我们都”知道”补码,但真正用到时却忘了它的存在
你发现下面代码中的问题了吗 ?
1 2 3 4 5 6 7 8 9
| bool ShouldSelect(int value, int mask) { if (-value & mask == Mathf.Abs(-value)) { return -value > 0; } return false; }
|
在C#中,A & B
和 -A & B
的区别主要在于对负数处理的不同。让我通过具体案例来说明:
案例演示
1 2 3 4 5 6 7 8 9 10
| int A = 5; int B = 3;
int result1 = A & B; Console.WriteLine($"A & B = {result1}");
int result2 = -A & B; Console.WriteLine($"-A & B = {result2}");
|
二进制分析:
1 2 3 4 5 6 7
| A = 5: 0000 0101 B = 3: 0000 0011 A & B: 0000 0001 = 1
-A = -5: 1111 1011 (补码表示) B = 3: 0000 0011 -A & B: 0000 0011 = 3
|
更多案例
1 2 3 4 5 6 7 8 9 10 11 12
| int A2 = 10; int B2 = 6;
Console.WriteLine($"{A2} & {B2} = {A2 & B2}"); Console.WriteLine($"-{A2} & {B2} = {-A2 & B2}");
int A3 = -3; int B3 = -5;
Console.WriteLine($"{A3} & {B3} = {A3 & B3}");
|
背后的意义
1. 补码表示法
- 在C#中,整数使用二进制补码表示
- 正数的补码是其本身
- 负数的补码 = 对应正数的二进制取反 + 1
2. 运算本质
A & B
:对两个数的实际二进制位进行按位与
-A & B
:先计算A的补码(得到-A),再与B进行按位与
3. 实际应用场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| [Flags] enum Permissions { Read = 1, Write = 2, Execute = 4 }
Permissions userPermissions = Permissions.Read | Permissions.Write;
bool canRead = (userPermissions & Permissions.Read) != 0;
bool wrongCheck = (-(int)userPermissions & (int)Permissions.Read) != 0;
|
4. 重要注意事项
1 2 3 4
| int A = 5, B = 3; Console.WriteLine($"-A & B = {-A & B}"); Console.WriteLine($"-(A & B) = {-(A & B)}");
|
总结
A & B
是直接的按位与运算,结果可预测
-A & B
涉及补码转换,结果取决于负数的二进制表示
- 在实际编程中,应避免对负数进行无意义的位运算,除非明确理解补码机制
- 位运算通常用于标志位、掩码操作等场景,应保持操作数的明确性和可读性
本文标题:补码陷阱
文章作者:Keyle
发布时间:2025-10-22
最后更新:2025-10-22
原始链接:https://vrast.cn/posts/55376/
版权声明:©Keyle's Blog. 本站采用署名-非商业性使用-相同方式共享 4.0 国际进行许可