负整数的二进制表示是理解计算机如何存储和处理负数的基石。主要有三种表示方法,其中最常用的是补码。
1. 原码
规则:最高位表示符号位(0为正,1为负),其余位表示数值的绝对值。
示例(8位表示):
1 2
| +5: 0000 0101 -5: 1000 0101
|
问题:
- 有
+0
(0000 0000) 和 -0
(1000 0000) 两种零
- 加减运算复杂,需要判断符号位
2. 反码
规则:
- 正数:与原码相同
- 负数:符号位为1,数值位按位取反
示例(8位表示):
1 2
| +5: 0000 0101 -5: 1111 1010 (5的原码 0000 0101 取反)
|
问题:
- 仍然存在
+0
(0000 0000) 和 -0
(1111 1111)
- 运算仍不够方便
3. 补码 ⭐(现代计算机标准)
这是现代计算机普遍使用的方法。
3.1. 补码的规则
对于正数:与原码相同
对于负数:有两种计算方法:
方法一:取反加一
- 写出对应正数的二进制
- 按位取反(0变1,1变0)
- 结果加1
方法二:用 2ⁿ 减去绝对值
其中 n 是位数(8位、16位、32位、64位)
3.2. 示例:求 -5 的 8位补码
方法一:取反加一
1 2 3
| +5: 0000 0101 取反: 1111 1010 加1: 1111 1011 ← -5的补码
|
方法二:公式计算
1 2
| 2⁸ - 5 = 256 - 5 = 251 251的二进制: 1111 1011
|
验证:1111 1011
就是 -5 的补码表示。
3.3. 更多例子(8位表示)
十进制 |
二进制(补码) |
计算过程 |
+7 |
0000 0111 |
直接表示 |
+1 |
0000 0001 |
直接表示 |
0 |
0000 0000 |
直接表示 |
-1 |
1111 1111 |
0000 0001 → 1111 1110 → 1111 1111 |
-2 |
1111 1110 |
0000 0010 → 1111 1101 → 1111 1110 |
-5 |
1111 1011 |
0000 0101 → 1111 1010 → 1111 1011 |
-128 |
1000 0000 |
特殊值(8位最小负数) |
4. 补码的优势
补码成为标准的原因:
4.1. 优势1:统一的加减法
减法可以转换为加法:
在8位系统中:
1 2 3
| 10: 0000 1010 -5: 1111 1011 相加: 1 0000 0101 = 5 ✓ (忽略进位)
|
4.2. 优势2:唯一的零
只有 0000 0000
表示零,没有负零。
4.3. 优势3:自然的溢出行为
1 2 3
| 127 + 1 = -128 -128 - 1 = 127
|
4.4. 优势4:范围对称且连续
对于 n 位有符号整数:
- 范围:
-2^(n-1)
到 2^(n-1)-1
- 8位:-128 到 127
- 16位:-32768 到 32767
- 32位:-2147483648 到 2147483647
5. 快速识别补码表示的负数
规则:最高位为1的数就是负数。
快速计算负数值:看到补码表示的负数,想要知道它的十进制值:
- 减1
- 取反
- 得到正数值,加上负号
示例:
1 2 3 4
| 看到: 1111 1011 (这是-5的补码) 减1: 1111 1010 取反: 0000 0101 = 5 结果: -5
|
6. C/C++ 中的验证代码
Demo1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <iostream> #include <bitset> using namespace std;
void print_binary_details(int num) { cout << "十进制: " << num << endl; cout << "二进制: " << bitset<8>(num) << endl; cout << "十六进制: 0x" << hex << (num & 0xFF) << dec << endl; cout << "---" << endl; }
int main() { print_binary_details(5); print_binary_details(-5); print_binary_details(0); print_binary_details(-1); print_binary_details(127); print_binary_details(-128); cout << "验证 10 + (-5) = 5:" << endl; int a = 10, b = -5; int result = a + b; cout << a << " + " << b << " = " << result << endl; cout << "10的二进制: " << bitset<8>(a) << endl; cout << "-5的二进制: " << bitset<8>(b) << endl; cout << "结果的二进制: " << bitset<8>(result) << endl; return 0; }
|
输出示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 十进制: 5 二进制: 00000101 十六进制: 0x5 --- 十进制: -5 二进制: 11111011 十六进制: 0xfb --- 十进制: 0 二进制: 00000000 十六进制: 0x0 --- 十进制: -1 二进制: 11111111 十六进制: 0xff ---
|
Demo2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include <bitset> #include <cstdint> #include <iostream>
int main() { int8_t a = 127; std::cout << "a: " << (int)a << ", bit:" << std::bitset<8>(a) << std::endl; int8_t b = -127; std::cout << "b: " << (int)b << ", bit:" << std::bitset<8>(b) << std::endl; int8_t c = a + 1; std::cout << "c: " << (int)c << ", bit:" << std::bitset<8>(c) << std::endl; c >>= 2; std::cout << "c: " << (int)c << ", bit:" << std::bitset<8>(c) << std::endl; int8_t d = -5; std::cout << "d: " << (int)d << ", bit:" << std::bitset<8>(d) << std::endl; d <<= 2; std::cout << "d: " << (int)d << ", bit:" << std::bitset<8>(d) << std::endl; int8_t e = -1; std::cout << "e: " << (int)e << ", bit:" << std::bitset<8>(e) << std::endl; e >>= 2; std::cout << "e: " << (int)e << ", bit:" << std::bitset<8>(e) << std::endl;
return 0; }
|
输出示例:
1 2 3 4 5 6 7 8
| a: 127, bit:01111111 b: -127, bit:10000001 c: -128, bit:10000000 c: -32, bit:11100000 d: -5, bit:11111011 d: -20, bit:11101100 e: -1, bit:11111111 e: -1, bit:11111111
|
7. 重要总结
- 现代计算机统一使用补码表示有符号整数
- 最高位是符号位:0表示正数,1表示负数
- 补码计算:正数直接表示,负数 = 对应正数取反加1
- 优势:统一的加减运算、唯一的零、连续的范围
- 范围:n位有符号整数的范围是
-2^(n-1)
到 2^(n-1)-1
理解补码对于进行位运算、处理溢出、优化性能等都至关重要!