日常刷题中,然后有个地方是要求将当前数变成绝对值才可以使用;然后开开心心写了如下代码:
long a = Math.abs(Integer.MIN_VALUE);
System.out.println(a);
结果结果却让我大跌眼镜。但是一番分析下来倒是很有意思
点开源码(JDK1.8环境下)
这段话翻译过来就是,
如果abs方法输入参数为正数,就返回那个值;如果未负数,就返回这个负数的对应绝对值;但是如果输入值为Integer.MIN_VALUE时;就返回输入值
这样看起来问题是解决了,但是身为一个合格的程序员,怎么可以不弄清楚原因呢。
1.原因分析
要讲清上面发生的原因,就必须把几个核心的问题都讲清楚;
1.1 原码
一开始人们是通过原码来表示整数,整数有正负之分,便通过符号位+二进制
的方式表示。(最高位(最左边)取0表示正数,取1表示负数。然后加上对应的二进制。
)
+1 = 0000 0001
-1 = 1000 0001
1.2 反码
正数的反码是其本身
负数的反码是符号位不变 ,其他位都变为反
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
1.3 补码
正数的补码就是其本身
负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
那么为何要使用补码呢?
因为计算机中没有减法的概念,对应减法 a-b = a+(-b)
;
反码的方式
1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
1 - 1 = 1 + (-1) = [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
补码的方式
1 - 1 = 1 + (-1) = [0000 0001]补 +[1111 1111]补 = [0000 0000]补=0
所以这里如果1.8的jdk不限制,就容易导致结果错误;可以理解为
MIN_VALUE[反] = 0x80000000 = 10....0(中间29个0)= 1......1(中间30个1)
MIN_VALUE[补] = 0x80000000 = 10....0(中间29个0) = 1......1(中间30个1)+1 = 0......0(中间30个1)
所以 这里错误了;而且可以发现超出限制后,会形成一个循环
所以可以理解为为了防止这样的循环发生,JDK1.8出现这个保护的模式;而一切的发生源头就是补码导致