Java 中如何表示负数,有符号和无符号的区别,补码的计算规则,char byte short int long 的范围
char、byte、short 最大值
| 类型 | 位数 | 最大值(十进制) | 最大值(十六进制) |
|---|---|---|---|
char |
16 位无符号 | 65535 | 0xFFFF / \uFFFF |
byte |
8 位有符号 | 127 | 0x7F |
short |
16 位有符号 | 32767 | 0x7FFF |
对应关系可以这样理解:
char:无符号 16 位,范围是0x0000~0xFFFF,最大值是0xFFFF。byte:有符号 8 位,范围是-128~127,最大值是0x7F。short:有符号 16 位,范围是-32768~32767,最大值是0x7FFF。
为什么 byte、short 是有符号
byte、short 是整数类型,Java 统一按有符号补码设计;char 不是普通整数,而是 16 位 Unicode 码元,所以必须无符号。
Java 的 byte、short、int、long 都采用二进制补码表示,最高位是符号位。
| 类型 | 位数 | 取值范围 |
|---|---|---|
byte |
8 位 | -128 ~ 127 |
short |
16 位 | -32,768 ~ 32,767 |
int |
32 位 | -2,147,483,648 ~ 2,147,483,647 |
long |
64 位 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 |
补码怎么表示负数
规则很简单:
- 正数:补码 = 原码。
- 负数:先写出对应正数的二进制,再按位取反,最后加 1。
以 8 位 byte 为例:
| 十进制 | 步骤 | 二进制 |
|---|---|---|
+3 |
正数原样 | 0000 0011 |
-3 |
0000 0011 取反 → 1111 1100,再加 1 |
1111 1101 |
-1 |
0000 0001 取反 → 1111 1110,再加 1 |
1111 1111 |
-128 |
特殊最小值 | 1000 0000 |
所以 byte 的范围是 -128 ~ 127,而不是对称的 -127 ~ 127。因为补码里 0 只占一个编码,负数那边就多出一个位置,于是最小负数比最大正数多 1。
为什么一定要用一位做符号位
计算机里只有 0 和 1,没有"+"和"-"这两个符号。要表示负数,就必须把"正负"这个信息也编码进二进制里。
最直观的做法就是拿最高位当符号位 :0 表示正,1 表示负。
| 8位二进制 | 无符号解读 | 有符号解读(补码) |
|---|---|---|
0111 1111 |
127 | +127 |
1000 0000 |
128 | -128 |
1111 1111 |
255 | -1 |
那为什么"牺牲"了正数范围?
以 8 位为例,256 个编码要分给正数、负数和零。补码的分配是:
- 1 个 给零(
0000 0000) - 127 个 给正数(
0000 0001~0111 1111) - 128 个 给负数(
1000 0000~1111 1111)
所以正数最大只有 127,而负数最小能到 -128。负数多一个,是因为零只占一个编码(不像原码有 +0 和 -0 两个),省下来的那个编码就归了负数。
用这一位换来了什么
- 减法变加法:a - b 等价于 a + (-b),CPU 只需要加法器,不需要单独的减法电路。
- 符号位参与运算:符号位和普通位一视同仁,不需要特殊判断。
- 零唯一:避免了 +0 和 -0 的歧义。
简单说:不是"浪费",而是"交易" ------用一半的正数范围,换来了负数表达能力和硬件运算的极大简化。如果不需要负数,直接用无符号类型(如 char)就好,那 16 位就能完整表示 0~65535。
举一个补码进行加法计算的例子
用一个 8 位 byte 的例子:计算 15 + (-25)。
1. 先写出两个数的补码
15 是正数,正数补码 = 原码:
text
15 的原码:0000 1111
15 的补码:0000 1111
-25 是负数,补码 = 对应正数按位取反再加 1:
text
25 的原码: 0001 1001
按位取反: 1110 0110
加 1: 1110 0111
所以:
text
-25 的补码:1110 0111
2. 两个补码直接相加
text
0000 1111 (15 的补码)
+ 1110 0111 (-25 的补码)
-----------
1111 0110
最高位没有产生额外进位,所以直接保留 1111 0110。
3. 把结果补码还原成原码
1111 0110 最高位是 1,说明它是负数的补码。还原方法:减 1,再按位取反。
text
1111 0110 - 1 = 1111 0101
按位取反: 0000 1010
所以结果是:
text
-10
验证:
text
15 + (-25) = -10 ✅
这个例子说明了什么
- 符号位也参与运算,不需要单独判断正负。
- 减法被统一成加法 :
15 + (-25)就是两个补码相加。 - 如果最高位产生进位,在固定位宽下直接丢弃。比如 4 位里
5 + (-2):0101 + 1110 = 1 0011,丢弃进位后得到0011,也就是3。
要不要说说补码溢出的判断条件?比如什么情况下结果会"绕回来"变成错误值。