日常刷題中季惩,然后有個(gè)地方是要求將當(dāng)前數(shù)變成絕對(duì)值才可以使用;然后開(kāi)開(kāi)心心寫(xiě)了如下代碼:
long a = Math.abs(Integer.MIN_VALUE);
System.out.println(a);
結(jié)果結(jié)果卻讓我大跌眼鏡腻格。但是一番分析下來(lái)倒是很有意思
點(diǎn)開(kāi)源碼(JDK1.8環(huán)境下)
這段話翻譯過(guò)來(lái)就是画拾,
如果abs方法輸入?yún)?shù)為正數(shù),就返回那個(gè)值荒叶;如果未負(fù)數(shù)碾阁,就返回這個(gè)負(fù)數(shù)的對(duì)應(yīng)絕對(duì)值;但是如果輸入值為Integer.MIN_VALUE時(shí)些楣;就返回輸入值
這樣看起來(lái)問(wèn)題是解決了脂凶,但是身為一個(gè)合格的程序員,怎么可以不弄清楚原因呢愁茁。
1.原因分析
要講清上面發(fā)生的原因蚕钦,就必須把幾個(gè)核心的問(wèn)題都講清楚;
1.1 原碼
一開(kāi)始人們是通過(guò)原碼來(lái)表示整數(shù)鹅很,整數(shù)有正負(fù)之分嘶居,便通過(guò)符號(hào)位+二進(jìn)制
的方式表示。(最高位(最左邊)取0表示正數(shù)促煮,取1表示負(fù)數(shù)邮屁。然后加上對(duì)應(yīng)的二進(jìn)制。
)
+1 = 0000 0001
-1 = 1000 0001
1.2 反碼
正數(shù)的反碼是其本身
負(fù)數(shù)的反碼是符號(hào)位不變 菠齿,其他位都變?yōu)榉?/p>
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
1.3 補(bǔ)碼
正數(shù)的補(bǔ)碼就是其本身
負(fù)數(shù)的補(bǔ)碼是在其原碼的基礎(chǔ)上, 符號(hào)位不變, 其余各位取反, 最后+1. (即在反碼的基礎(chǔ)上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]補(bǔ)
[-1] = [10000001]原 = [11111110]反 = [11111111]補(bǔ)
那么為何要使用補(bǔ)碼呢佑吝?
因?yàn)橛?jì)算機(jī)中沒(méi)有減法的概念,對(duì)應(yīng)減法 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
補(bǔ)碼的方式
1 - 1 = 1 + (-1) = [0000 0001]補(bǔ) +[1111 1111]補(bǔ) = [0000 0000]補(bǔ)=0
所以這里如果1.8的jdk不限制绳匀,就容易導(dǎo)致結(jié)果錯(cuò)誤芋忿;可以理解為
MIN_VALUE[反] = 0x80000000 = 10....0(中間29個(gè)0)= 1......1(中間30個(gè)1)
MIN_VALUE[補(bǔ)] = 0x80000000 = 10....0(中間29個(gè)0) = 1......1(中間30個(gè)1)+1 = 0......0(中間30個(gè)1)
所以 這里錯(cuò)誤了;而且可以發(fā)現(xiàn)超出限制后疾棵,會(huì)形成一個(gè)循環(huán)
所以可以理解為為了防止這樣的循環(huán)發(fā)生戈钢,JDK1.8出現(xiàn)這個(gè)保護(hù)的模式;而一切的發(fā)生源頭就是補(bǔ)碼導(dǎo)致