1. 疑问
最近在刷Leetcode的时候,遇到这样一个题:
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围[,]。请根据这个假设,如果反转后整数溢出那么就返回 0。
读完题后问了自己几个问题,发现自己都不清楚:
- 32位整数是多少?32位是指什么?
- 32为有符号整数的范围为什么是[,]?
- 二进制怎么表示?
掏出计算器用二进制算了一下,=4294967295,=2147483647
到这里,上面的问题基本上搞明白了,新的问题来了:
- 二进制怎么表示正负数
- 二进制的正负数怎么计算,计算机是怎么处理的
- 什么是原码?反码?补码?
- Java中是如何处理Integer的?
2. 开始入正题
2.1. 二进制、原码、反码、补码
为什么要有补码?
这里以4位bit为例,二进制最高位0代表+,1代表-
整数 | 原码 |
---|---|
4 | 0 100 |
-4 | 1 100 |
0 | 0000 |
5 | 0 101 |
-5 | 1 101 |
2 | 0010 |
7 | 0 111 |
-7 | 1 111 |
5 | 0110 |
因为计算机中没有减法,我们发现,这些数的正负数之和,只有4的二进制计算是正确的,结果为0。为了解决这个问题,所以就有了反码、补码的概念。
整数 | 原码 | 反码 | 补码 |
---|---|---|---|
4 | 0 100 | 0 100 | 0 100 |
-4 | 1 100 | 1 011 | 1 100 |
0 | - | - | 0000 |
5 | 0 101 | 0 101 | 0 101 |
-5 | 1 101 | 1 010 | 1 011 |
0 | - | - | 0000 |
7 | 0 111 | 0 111 | 0 111 |
-7 | 1 111 | 1 000 | 1 001 |
0 | - | - | 0000 |
通过补码进行正负数的计算,结果就对了
2.1.1. 原码
最高位表示正负号,0代表整数,1代表负数,其它位表示数值的二进制
2.1.2. 反码
- 正数的反码:与原码一致
- 负数的反码:最高位符号位不变,其它位取反
2.1.3. 补码
- 正数的补码:与原码一致
- 负数的补码:反码 + 1
2.2. Java中的Integer
@Test
void testLocal() {
Integer maxInt = Integer.MAX_VALUE;
Integer minInt = Integer.MIN_VALUE;
log.info("十进制:min = {}, max = {}", minInt, maxInt);
String minBinary = Integer.toBinaryString(minInt);
String maxBinary = Integer.toBinaryString(maxInt);
log.info("二进制:minBinary {}位 = {}, maxBinary {}位 = {}", minBinary.length(), minBinary, maxBinary.length(), maxBinary);
log.info("int 0 = binary {}", Integer.toBinaryString(0));
log.info("{} = {}, {} = {}", minInt + 1, Integer.toBinaryString(minInt + 1), maxInt - 1, Integer.toBinaryString(maxInt - 1));
log.info("越界intMax + 1 = {}", maxInt + 1);
}
复制代码
Java中的Integer的大小是32位bit,最小值和最大值范围就是[,],数值大小是31位,最高位0/1表示正负。
从二进制的角度来看,对正整数的最大值是31位1,加1后所有位进1变为0,最后结果就是最高位由0 -> 1,得到的二进制结果和负数的最小值相同,这样就容易理解java中的整数越界,以及Integer的范围。
3. 总结
- 计算机中的计算只有加法和二进制,为了原码、反码、补码就是为了解决计算机中的运算
- 整数越界,就是在固定32位长度下,对二进制计算结果转为整数的过程
- 通过一个问题引申出一系列的问题,从数值范围 -> 二进制表示 -> 有/无符号 -> 二进制计算 -> 原码/反码/补码 -> 整数越界...
4. 最后
了解的二进制后,Java中的移位运算符("<<"、">>"、">>>")是怎么做运算的?