计算机基础系列文章
计算机基础(一):ASCll、GB2312、GBK、Unicode、UTF-32、UTF-16、UTF-8深度解析
在Java的世界里,我们每天都在与整数打交道:int age = 30;
、long balance = 1000000L;
。但你是否思考过,这些数字在计算机内部的真实形态?理解原码、反码、补码
不仅是计算机科学的基础,更是深入Java底层、避免隐蔽bug的关键。本文将带你彻底掌握这些二进制表示法的奥秘及其在Java中的实际应用。
计算机只能处理二进制(0和1)。如何表示有符号整数
(正数、负数)?工程师们设计了三种方案
:
定义:最高位表示符号位
(0=正数,1=负数),其余位表示数值的绝对值
示例 (8位byte为例):
0000 0101
(符号位0,绝对值5)1000 0101
(符号位1,绝对值5)优点:人类直观,理解容易
致命缺陷:
存在两种零
:+0
的原码为00000000
,-0
的原码为10000000
( 以一个字节
长表示 ),浪费表示范围,逻辑上冗余
加减运算复杂
:正数和负数相加,数值部分实际上是相减,符号取决于绝对值大者的符号,硬件不能直接相加符号位和数值位
示例1:(+5) + (-5) = 0,程序直接运算❌
原码的加法不支持符号修正
(不能自动让结果的符号与数值正确对应)
0000 0101 (+5)
+ 1000 0101 (-5)
-----------------
1000 1010 结果为 -10
示例2:(-5) + (+3) = -2,程序直接运算❌
1000 0101 (-5)
+ 0000 0011 (+3)
-----------------
1000 1000 结果为 -8
示例3:(-5) + (-5) = -10,程序直接运算❌
原码的加法不支持进位消除
(多出的进位被丢弃,不影响结果)
1000 0101 (-5)
+ 1000 0101 (-5)
-----------------
1 0000 1010 结果为 10(注意:最高位溢出了1位舍弃)
定义:
反码 = 原码
负数原码符号位不变,数值位按位取反
,或者更简单点正数原码全部按位取反
示例 (8位byte为例):
0000 0101
(同原码)1111 1010
(-5的原码1000 0101
按位取反,符号位不变或者+5原码全部按位取反)遗留问题:
两种零依然存在
:+0
反码为0000 0000
,-0
反码1111 1111
(+0的原码按位取反)
循环进位
:计算结果最高位有进位
时,需要把进位“回加”到最低位(称为“末位加1”或“循环进位”),硬件实现仍不理想
示例1:(+5) + (-5) = 0,程序直接运算✅
反码的加法在数值上可以看作正确,但从表达角度来看,有点不完美
0000 0101 (+5)
+ 1111 1010 (-5)
-----------------
1111 1111 结果是反码,符号位不变其他按位取反,原码就是1000 0000也就是-0(负零)
示例2:(-5) + (+3) = -2,程序直接运行✅
1111 1010 (-5)
+ 0000 0011 (+3)
-----------------
1111 1101 结果是反码,原码为1000 0010也就是-2
示例3:(-5) + (-5) = -10,程序直接运行❌
反码的加法需要循环进位
✅
1111 1010 (-5)
+ 1111 1010 (-5)
-----------------
1 1111 0100 结果是反码(注意:最高位溢出了1位舍弃),最高位进位:1(需循环加回最低位)
1111 0100
+ 1
-----------------
1111 0101 这里还是反码,原码为1000 1010也就是-10
定义:
补码 = 原码
补码 = 反码 + 1
示例 (8位byte为例):
0000 0101
1111 1010
,再加1获得补码1111 1011
核心优势 (完美解决前两者问题):
唯一的零:+0
补码为0000 0000
,-0
补码是+0的原码全部按位取反再加1得到还是0000 0000
,溢出一位舍去
减法变加法:A - B = A + (-B)
直接成立,无需额外判断符号位
或处理循环进位
。硬件只需一套加法电路
示例1:(+5) + (-5) = 0,程序直接运算✅
补码加法支持进位消除
(多出的进位被丢弃,不影响结果)
0000 0101 (+5)
+ 1111 1011 (-5)
-----------------
1 0000 0001 结果是补码,先减1获得反码0000 0000,也就是0
示例2:(-5) + (+3) = -2,程序直接运算✅
1111 1011 (-5)
+ 0000 0011 (+3)
-----------------
1111 1110 结果是补码,先减1获得反码1111 1101,符号位不变其他按位取反获取原码1000 0010,也就是-2
示例3:(-5) + (-5) = -10,程序直接运算✅
1111 1011 (-5)
+ 1111 1011 (-5)
-----------------
1 1111 0110 结果为是补码,先减1获得反码1111 0101,符号位不变其他按位取反获得原码1000 1010,也就是-10
双零
、复杂运算
),简化了CPU硬件设计,提高了运算效率 byte
, short
, int
, long
)均使用补码表示!这是现代计算机体系结构的标准8 位二进制的表示能力
2^8 = 256
种可能的二进制组合7
位表示数值补码表示法的规则
1000 0000
被定义为 -128
二进制(补码) | 十进制值 |
---|---|
1000 0000 | -128 |
1000 0001 | -127 |
1000 0010 | -126 |
... | … |
1111 1110 | -2 |
1111 1111 | -1 |
0000 0000 | 0 |
0000 0001 | 1 |
0000 0010 | 2 |
... | … |
0111 1110 | 126 |
0111 1111 | 127 |
为何不是 -127 到 127?
个值,无法覆盖全部 256 种组合
1000 0000
分配给 -128 后: 0111 1111
)+1 溢出为 -128(1000 0000
),实现连续循环
1000 0000
),且破坏数值连续性
,-127(1000 0001
)-1正好是-128(1000 0000
)编程语言中的实际表现
127 + 1 = -128
(因 0111 1111 + 1 = 1000 0000
)Integer.toBinaryString(int i)
方法会返回一个整数补码表示的字符串(省略前导零,负数显示完整的32位,int占4个字节
)public class ComplementDemo {
public static void main(String[] args) {
int positive = 5;
int negative = -5;
// 打印正数5的二进制(补码,省略前导零)
System.out.println(Integer.toBinaryString(positive)); // 输出: 101
// 打印负数-5的二进制(32位完整补码)
System.out.println(Integer.toBinaryString(negative)); // 输出: 1111 1111 1111 1111 1111 1111 1111 1011
}
}
解读负数输出:
11111111111111111111111111111011
就是 -5 的 32 位补码00000000_00000000_00000000_00000101
) 按位取反 (11111111_11111111_11111111_11111010
)11111111_11111111_11111111_11111011
)A - B = A + (-B)
,硬件实现高效简单,是现代计算机整数表示的标准byte
, short
, int
, long
均用补码。Integer.toBinaryString()
可查看补码形式