首页 > 代码库 > 【java解惑】java中那些反常识的小知识(续)

【java解惑】java中那些反常识的小知识(续)


六、Q:请为 i!=0 && i ==-1 提供一个声明,使得其成立。


    分析:在布尔表达式(i != 0 && i == -i)中,一元减号操作符作用于 i,这意味着它的类型必须是数字型的:一元减号操作符作用于一个非数字型操作数是非法的。因此,我们要寻找一个非 0 的数字型数值,它等于它自己的负值。NaN 不能满足这个属性,因为它不等于任何数值,因此,i 必须表示一个实际的数字。肯定没有任何数字满足这样的属性吗?嗯, 没有任何实数具有这种属性,但是没有任何一种 Java 数值类型能够对实数进行完美建模。 浮点数值是用一个符号位、一个被通俗地称为尾数(mantissa)的有效数字以及一个指数来表示的。除了 0 之外,没有任何浮点数等于其符号位反转之后的值,因此 i 的类型必然是整数型的。有符号的整数类型使用的是 2 的补码算术运算:为了对一个数值取其负值,你要反转其每一位,然后加 1。2 的补码算术运算的一个很大的优势是,0 具有唯一的表示形式。如果你要对 int 数值 0 取负值,你将得到 0xffffffff+1,它仍然是 0。但是,这也有一个相应的不利之处,总共存在偶数个 int 数值(准确地说有2^32 个),其中一个用来表示 0,这样就剩下奇数个 int 数值来表示正整数和负整数,这意味着正的和负的 int 数值的数量必然不相等。这暗示着至少有一个 int数值,其负值不能正确地表示成为一个 int 数值。事实上, 恰恰就有一个这样的 int 数值,它就是 Integer.MIN_VALUE,即-2^31。它的十六进制表示是 0x80000000。其符号位为 1,其余所有的位都是 0。如果我们对这个值取负值,那么我们将得到 0x7fffffff+1, 也就是 0x80000000,即Integer. MIN_VALUE! 换句话说,Integer.MIN_VALUE 是它自己的负值,Long.MIN_VALUE 也是一样。对这两个值取负值将会产生溢出,但是 Java 在整数计算中忽略了溢出。


    A:下面的声明将使得布尔表达式(i != 0 && i == -i)的计算结果为true:int i = Integer.MIN_VALUE;下面这个也可以:long i = Long.MIN_VALUE;


    总结:如果你对取模运算很熟悉,那么很有必要指出,这个谜题也可以用代数方法解决。Java 的 int 算术运算是实际的算术运算对 2^32 取模的运算,因此本谜题需要一个对这种线性全等的非 0 解决方案:i ≡ -i(mod 2^32);将 i 加到恒等式的两边,我们可以得到:2i ≡ 0(mod 2^32);对这种全等的非 0 解决方案就是 i = 2^31。尽管这个值不能表示成为一个 int,但是它是和-2^31 全等的,即与 Integer.MIN_VALUE 全等。总之,Java 使用 2 的补码的算术运算,它是非对称的。对于每一种有符号的整数类型(int、long、byte 和 short),负的数值总是比正的数值多一个,这个多出来的值总是这种类型所能表示的最小数值。对 Integer.MIN_VALUE取负值得到的还是它没有改变过的值,Long.MIN_VALUE 也是如此。对Short. MIN_VALUE取负值并将所产生的 int 数值转型回 short,返回的同样是最初的值(Short.MIN_VALUE)。对 Byte.MIN_VALUE 来说,也会产生相似的结果。




注:本【java解惑】系列均是博主阅读《java解惑》原书后将原书上的讲解和例子部分改编然后写成博文进行发布的。所有例子均亲自测试通过并共享在github上。通过这些例子激励自己惠及他人。同时本系列所有博文会同步发布在博主个人微信公众号搜索“爱题猿”或者“ape_it”方便大家阅读。如果文中有任何侵犯原作者权利的内容请及时告知博主以便及时删除如果读者对文中的内容有异议或者问题欢迎通过博客留言或者微信公众号留言等方式共同探讨。

源代码地址https://github.com/rocwinger/java-disabuse


本文出自 “winger” 博客,谢绝转载!

【java解惑】java中那些反常识的小知识(续)