請?zhí)峁┮粋€對i的聲明,將下面的循環(huán)轉(zhuǎn)變?yōu)橐粋€無限循環(huán)。這個循環(huán)不需要使用任何5.0版的特性:
while (i != 0 && i == -i) {
}
這仍然是一個循環(huán)。在布爾表達(dá)式(i != 0 && i == -i)中,一元減號操作符作用于i,這意味著它的類型必須是數(shù)字型的:一元減號操作符作用于一個非數(shù)字型操作數(shù)是非法的。因此,我們要尋找一個非0的數(shù)字型數(shù)值,它等于它自己的負(fù)值。NaN不能滿足這個屬性,因?yàn)樗坏扔谌魏螖?shù)值,因此,i必須表示一個實(shí)際的數(shù)字。肯定沒有任何數(shù)字滿足這樣的屬性嗎?
嗯,沒有任何實(shí)數(shù)具有這種屬性,但是沒有任何一種Java數(shù)值類型能夠?qū)?shí)數(shù)進(jìn)行完美建模。浮點(diǎn)數(shù)值是用一個符號位、一個被通俗地稱為尾數(shù)(mantissa)的有效數(shù)字以及一個指數(shù)來表示的。除了0之外,沒有任何浮點(diǎn)數(shù)等于其符號位反轉(zhuǎn)之后的值,因此i的類型必然是整數(shù)型的。
有符號的整數(shù)類型使用的是2的補(bǔ)碼算術(shù)運(yùn)算:為了對一個數(shù)值取其負(fù)值,你要反轉(zhuǎn)其每一位,然后加1,從而得到結(jié)果[JLS 15.15.4]。2的補(bǔ)碼算術(shù)運(yùn)算的一個很大的優(yōu)勢是,0具有的表示形式。如果你要對int數(shù)值0取負(fù)值,你將得到0xffffffff+1,它仍然是0。
但是,這也有一個相應(yīng)的不利之處,總共存在偶數(shù)個int數(shù)值——準(zhǔn)確地說有232個——其中一個用來表示0,這樣就剩些奇數(shù)個int數(shù)值來表示正整數(shù)和負(fù)整數(shù),這意味著正的和負(fù)的int數(shù)值的數(shù)量必然不相等。這暗示著至少有一個int數(shù)值,其負(fù)值不能正確地表示成為一個int數(shù)值。
事實(shí)上,恰恰就有一個這樣的int數(shù)值,它就是Integer.MIN_VALUE,即-231。他的十六進(jìn)制表示是0x80000000。其符號位為1,其余所有的位都是0。如果我們對這個值取負(fù)值,那么我們將得到0x7fffffff+1,也就是0x80000000,即Integer.MIN_VALUE!換句話說,Integer.MIN_VALUE是它自己的負(fù)值,Long.MIN_VALUE也是一樣。對這兩個值取負(fù)值將會產(chǎn)生溢出,但是Java在整數(shù)計(jì)算中忽略了溢出。其結(jié)果已經(jīng)闡述清楚了,即使它們并不總是你所期望的。
下面的聲明將使得布爾表達(dá)式(i != 0 && i == -i)的計(jì)算結(jié)果為true,從而使循環(huán)無限環(huán)繞下去:
int i = Integer.MIN_VALUE;
下面這個也可以:
long i = Long.MIN_VALUE;
如果你對取模運(yùn)算很熟悉,那么很有必要指出,這個謎題也可以用代數(shù)方法解決。Java的int算術(shù)運(yùn)算是實(shí)際的算術(shù)運(yùn)算對232取模的運(yùn)算,因此本謎題需要一個對這種線性全等的非0解決方案:
i ≡ -i(mod 232)
將i加到恒等式的兩邊,我們可以得到:
2i ≡ 0(mod 32)
對這種全等的非0解決方案就是 i = 231。盡管這個值不能表示成為一個int,但是它是和-231全等的,即與Integer.MIN_VALUE全等。
總之,Java使用2的補(bǔ)碼的算術(shù)運(yùn)算,它是非對稱的。對于每一種有符號的整數(shù)類型(int、long、byte和short),負(fù)的數(shù)值總是比正的數(shù)值多一個,這個多出來的值總是這種類型所能表示的最小數(shù)值。對Integer.MIN_VALUE取負(fù)值得到的還是它沒有改變過的值,Long.MIN_VALUE也是如此。對Short.MIN_VALUE取負(fù)值并將所產(chǎn)生的int數(shù)值轉(zhuǎn)型回short,返回的同樣是最初的值(Short.MIN_VALUE)。對Byte.MIN_VALUE來說,也會產(chǎn)生相似的結(jié)果。更一般地講,千萬要當(dāng)心溢出:就像狼人一樣,它是個殺手。
對語言設(shè)計(jì)者的教訓(xùn)與謎題26中的教訓(xùn)一樣。應(yīng)該對某種溢出不會悄悄發(fā)生的整數(shù)算術(shù)運(yùn)算形式提供語言級的支持。
while (i != 0 && i == -i) {
}
這仍然是一個循環(huán)。在布爾表達(dá)式(i != 0 && i == -i)中,一元減號操作符作用于i,這意味著它的類型必須是數(shù)字型的:一元減號操作符作用于一個非數(shù)字型操作數(shù)是非法的。因此,我們要尋找一個非0的數(shù)字型數(shù)值,它等于它自己的負(fù)值。NaN不能滿足這個屬性,因?yàn)樗坏扔谌魏螖?shù)值,因此,i必須表示一個實(shí)際的數(shù)字。肯定沒有任何數(shù)字滿足這樣的屬性嗎?
嗯,沒有任何實(shí)數(shù)具有這種屬性,但是沒有任何一種Java數(shù)值類型能夠?qū)?shí)數(shù)進(jìn)行完美建模。浮點(diǎn)數(shù)值是用一個符號位、一個被通俗地稱為尾數(shù)(mantissa)的有效數(shù)字以及一個指數(shù)來表示的。除了0之外,沒有任何浮點(diǎn)數(shù)等于其符號位反轉(zhuǎn)之后的值,因此i的類型必然是整數(shù)型的。
有符號的整數(shù)類型使用的是2的補(bǔ)碼算術(shù)運(yùn)算:為了對一個數(shù)值取其負(fù)值,你要反轉(zhuǎn)其每一位,然后加1,從而得到結(jié)果[JLS 15.15.4]。2的補(bǔ)碼算術(shù)運(yùn)算的一個很大的優(yōu)勢是,0具有的表示形式。如果你要對int數(shù)值0取負(fù)值,你將得到0xffffffff+1,它仍然是0。
但是,這也有一個相應(yīng)的不利之處,總共存在偶數(shù)個int數(shù)值——準(zhǔn)確地說有232個——其中一個用來表示0,這樣就剩些奇數(shù)個int數(shù)值來表示正整數(shù)和負(fù)整數(shù),這意味著正的和負(fù)的int數(shù)值的數(shù)量必然不相等。這暗示著至少有一個int數(shù)值,其負(fù)值不能正確地表示成為一個int數(shù)值。
事實(shí)上,恰恰就有一個這樣的int數(shù)值,它就是Integer.MIN_VALUE,即-231。他的十六進(jìn)制表示是0x80000000。其符號位為1,其余所有的位都是0。如果我們對這個值取負(fù)值,那么我們將得到0x7fffffff+1,也就是0x80000000,即Integer.MIN_VALUE!換句話說,Integer.MIN_VALUE是它自己的負(fù)值,Long.MIN_VALUE也是一樣。對這兩個值取負(fù)值將會產(chǎn)生溢出,但是Java在整數(shù)計(jì)算中忽略了溢出。其結(jié)果已經(jīng)闡述清楚了,即使它們并不總是你所期望的。
下面的聲明將使得布爾表達(dá)式(i != 0 && i == -i)的計(jì)算結(jié)果為true,從而使循環(huán)無限環(huán)繞下去:
int i = Integer.MIN_VALUE;
下面這個也可以:
long i = Long.MIN_VALUE;
如果你對取模運(yùn)算很熟悉,那么很有必要指出,這個謎題也可以用代數(shù)方法解決。Java的int算術(shù)運(yùn)算是實(shí)際的算術(shù)運(yùn)算對232取模的運(yùn)算,因此本謎題需要一個對這種線性全等的非0解決方案:
i ≡ -i(mod 232)
將i加到恒等式的兩邊,我們可以得到:
2i ≡ 0(mod 32)
對這種全等的非0解決方案就是 i = 231。盡管這個值不能表示成為一個int,但是它是和-231全等的,即與Integer.MIN_VALUE全等。
總之,Java使用2的補(bǔ)碼的算術(shù)運(yùn)算,它是非對稱的。對于每一種有符號的整數(shù)類型(int、long、byte和short),負(fù)的數(shù)值總是比正的數(shù)值多一個,這個多出來的值總是這種類型所能表示的最小數(shù)值。對Integer.MIN_VALUE取負(fù)值得到的還是它沒有改變過的值,Long.MIN_VALUE也是如此。對Short.MIN_VALUE取負(fù)值并將所產(chǎn)生的int數(shù)值轉(zhuǎn)型回short,返回的同樣是最初的值(Short.MIN_VALUE)。對Byte.MIN_VALUE來說,也會產(chǎn)生相似的結(jié)果。更一般地講,千萬要當(dāng)心溢出:就像狼人一樣,它是個殺手。
對語言設(shè)計(jì)者的教訓(xùn)與謎題26中的教訓(xùn)一樣。應(yīng)該對某種溢出不會悄悄發(fā)生的整數(shù)算術(shù)運(yùn)算形式提供語言級的支持。