发现
Java int 范围 -2147483648 ~ 2147483647
执行一下代码发现:
int a = 2147483647;
int b = a + 1;
int c = b - 1;
sout(b) // b = -2147483648
sout(c) // c = 2147483647
b 是 -2147483648
c 是 2147483647
似乎是一个循环?
为什么
上边这个链接看完知道,cpu二进制计算不对原码进行计算, 而是对补码进行计算。
就是说计算机的运算方式并不完全是按照人类的计算方式来的即 1+1有时候不是等于2
其实应该是初学时就应该研究的问题。只是在学校没好好学
在这个基础上
补码运算
十进制 | 八进制 | 原码 | 补码 |
---|---|---|---|
2147483647 | 7FFFFFFF | 0111 1111 1111 1111 1111 1111 1111 1111 | 0111 1111 1111 1111 1111 1111 1111 1111 |
1 | 1 | 0000 0000 0000 0000 0000 0000 0000 0001 | 0000 0000 0000 0000 0000 0000 0000 0001 |
-1 | FFFFFFFF | 1000 0000 0000 0000 0000 0000 0000 0001 | 1111 1111 1111 1111 1111 1111 1111 1111 |
-2147483647 | 80000001 | 1111 1111 1111 1111 1111 1111 1111 1111 | 1000 0000 0000 0000 0000 0000 0000 0001 |
-2147483648 | 80000000 | 32位无法表示 | 1000 0000 0000 0000 0000 0000 0000 0000 |
1. 首位为符号位 “0 - 正”, “ 1 - 负”
2. 正数的补码就是它本身
3. 负数的补码是 原码符号位不变,各位取反,末尾加1
回到开始的发现
java中 int 是 32位 ,也就是说最多最多可以保存二进制32位,如果计算所过程中超出这个范围那么计算机的计算方式就与人类不同,即可能发生 1+1不等于2的问题.
这里的范围看JAVA Integer.java 源码
/**
* A constant holding the minimum value an {@code int} can
* have, -2<sup>31</sup>.
*/
@Native public static final int MIN_VALUE = 0x80000000;
/**
* A constant holding the maximum value an {@code int} can
* have, 2<sup>31</sup>-1.
*/
@Native public static final int MAX_VALUE = 0x7fffffff;
转成补码的意义对于计算机来说就是,只有 加 运算
比如 1 - 1 就是:1 的补码加上 -1 的补码
32位运算, 即:
0000 0000 0000 0000 0000 0000 0000 0001
+
1111 1111 1111 1111 1111 1111 1111 1111=
1 0000 0000 0000 0000 0000 0000 0000 0000
只能保存 32bit 的数据,所以 1 被丢弃
上式=
0000 0000 0000 0000 0000 0000 0000 0000
这样计算机就可以仅通过加法且忽略符号直接计算出结果
明白了这些,现在再计算一下 JAVA 中 int 的 2147483647 + 1
首先两者转换成二进制原码,然后得出其补码,正数的补码是他本身
即:
0111 1111 1111 1111 1111 1111 1111 1111
+
0000 0000 0000 0000 0000 0000 0000 0001=
1000 0000 0000 0000 0000 0000 0000 0000
这个数字就非常特别了,零的机内表示形式
即
正零 = 0000 0000 0000 0000 0000 0000 0000 0000
负零 = 1000 0000 0000 0000 0000 0000 0000 0000
也就是说 补码32位表示的整数范围要比原码表示范围多一个数 即 -0
所以为了不浪费资源把这个[1000 0000 0000 0000 0000 0000 0000 0000]补人为规定为 -2147483648,但-2147483648在32位中是没有补码的。
同理,8位 [1000 0000]补 人为规定为 -128
那么,最后再计算一个 -2147483648 -1
即,
-2147483648 + (-1)
=
1000 0000 0000 0000 0000 0000 0000 0000
+
1111 1111 1111 1111 1111 1111 1111 1111=
1 0111 1111 1111 1111 1111 1111 1111 1111
最高位溢出,舍掉=
0111 1111 1111 1111 1111 1111 1111 1111
那么,最高位是0 是正数,正数的原码和补码一致,所以他的十进制数就是:2147483647
破案了
也就是说,int 类型的数据被设计成只能存储二进制32bit的数据,由于计算机存储数据是通过补码形式,且补码表示整数范围比原码多一个-0,导致int类型的表示范围是 -2147483648 ~ 2147483647。又因为int类型的32位数据限制最高位是符号位,溢出的数据被丢弃,导致数据精度丢失,表现出来的现象就是 2147483647加一是-2147483648
以上为个人理解的思路,在查资料过程中发现已经有大神详细的说明了此处的疑惑:-2147483648~2147483647–深入计算机内存运算了解二进制本质