1. 程式人生 > >【原創】JAVA中令人眼花撩亂的數字魔法

【原創】JAVA中令人眼花撩亂的數字魔法

五月的深圳空氣中瀰漫起初夏的味道,淡淡的,暖暖的。春日裡不太張揚的陽光也摻入這股氣息...(煙哥好文采!)
這天,煙哥愉快的喝著霸氣芝士莓莓莓。一邊東張西望,尋找著可以裝13的機會。一切正如下面這張圖這樣

這時,小劉出現了!沒錯,就是那個大家期待的小劉出現了!她拿著一本《XXXjava筆試指南》來找煙哥。
只見小劉嫻熟的開啟這本書,望著整本書滿滿的筆記,煙哥不禁猜測道:"小劉如此熱衷於學習,一定還是單身。"想到這裡,煙哥不禁更有回答的動力了(沒錯,我就是這種人!)。

快取問題

小劉翻到某一頁後,指出下面這樣一道題

public static void main(String[] args){
 Integer a = 50;
 Integer b = 50;
 Integer c = 150;
 Integer d = 150;
 System.out.println(a==b);
 System.out.println(c==d);
}

輸出結果為

true
false

然後詢問煙哥具體緣由。
煙哥看完以後,內心正(wei)直(suo)的笑了笑,心裡想道:"這不是幾年前的老題目了麼,怎麼現在還在考!"
煙哥回答道:"其實很簡單,原理是下面這樣滴!"
JAVA編譯器編譯Integer a = 50的時候,被翻譯成-> Integer a = Integer.valueOf(50);
valueOf的原始碼是下面這樣的

 public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
  }

看到了嘛,Integer內部有一個IntegerCache快取。對於值範圍在-128到127之間的數,會進行快取。因此a和b範圍在-128到127之間,所以指向的是同一個物件,所以判斷結果是true。而c和d在128之外,所以每次都是返回一個新物件,所以判斷結果是false
聽到這裡,小劉似乎很滿意的準備打道回府。
"不行,怎麼能這樣讓小劉離開。我得多和小劉說說話!"
於是,煙哥說道:"小劉阿,其實java筆試裡關於數值方面的陷阱,可以玩出很多花樣,你瞭解多少?"
很自然的,小劉的表情是下面這樣的

越界問題

煙哥說道:"這樣吧,先問你一個問題。Math.abs(Integer.MIN_VALUE))

的結果是正數還是負數?"
小劉:"Math.abs是絕對值運算,結果應該是正數吧。"
煙哥:"不對,還是負數。Integer的範圍為-2147483648~2147483647。不過我們先來看一眼abs函式的原始碼,如下所示"

public static int abs(int a) {
    return (a < 0) ? -a : a;
}

煙哥:"看了原始碼,其實很明顯,絕對值運算的原理是判斷這個數是否大於零,如果小於零則取負值。OK,回到我們題目。Integer.MIN_VALUE,它的十六進位制表示是 0x80000000。其符號位為1,其餘所有的位都是0。取負數(反碼+1)則為 0x7fffffff+1,也就是 0x80000000。你會發現對Integer.MIN_VALUE取負值還是本身。因此,結果還是負數。"
小劉:"那你這套理論對LongShortByte都成立麼?"
煙哥:"是的,都是成立的,原理都一樣。你可以回去測試一下如下程式碼"

Short num =(short)Math.abs(Short.MIN_VALUE));
System.out.println(num);

小劉望著這些程式碼,陷入了思考...
突然,小劉回答道:"好像,之前我有看到一個題目是這樣的。是否存在一個數i,可以使得i+1<i,這樣看來,這個i就是Integer.MAX_VALUE,因為加完1就溢位了變為負值了。"
聽小劉說道這裡,煙哥換了一個角度問:"是否存在一個數,滿足i != 0 && i == -i"
小劉想了下,機智的回答道:"其實還是Integer.MIN_VALUE,原因你剛才說過了!"
唉,沒想到小劉領悟如此的快!

浮點奧祕

煙哥感慨小劉領悟速度的同時,加大難度問道:"是否存在一個數,滿足i==i+1?"
小劉突然懵了,答道:"好像..似乎..應該一個數永遠不會等於自己加一。"
看見小劉懵圈的眼神,煙哥只見自己裝13的目的已經達到,便不再賣關子...
煙哥回答道:"有沒聽過一句話,無窮大加一個常數還是無窮大!所以,下面的例子輸出為true"

double i = Double.POSITIVE_INFINITY;
System.out.println(i == i+1);

小劉反懟煙哥,說道:"其實,無窮大減去一個常數也是無窮大。所以下面例子也是輸出為true,而且無窮小也有類似的特性。"

double i = Double.POSITIVE_INFINITY;
System.out.println(i == i-1);

煙哥滿意的點了點頭,感慨小劉成長真快!
突然,靈光一閃,煙哥補充道:"你知不知道有一個數可以滿足i!=i?"
小劉再次陷入了深思...嘴裡嘟囔道:"奇怪,還有一個數可以自己不等於自己麼?"
望著小劉愁眉苦臉的眼神 ,煙哥答道:"對,有一個不是數值的值,它就是NaN,翻譯過來就是(Not a Number),因此下面的輸出是為true!"

double i = Double.NaN;
System.out.println(i != i);

結局

在煙哥一陣裝13後,決定暴露自己的本性。問道:"小劉,你有物件了麼?"
小劉答道:"煙哥,你是個好人,然而我已經有物件了!"
"Boom!"

(本文完