Java - 类型转换问题

3
long a = (long)Math.pow(2, 32);
// a = 4294967296 :)

long a = (int)(long)Math.pow(2, 32);
//a = 0 ?!

long a = (int)Math.pow(2, 32);
//a = 2147483647 WTF??!!!

第一个表达式很明显,a会按照原样打印出来。
第二个表达式有点令人困惑。大数值是100000000000000000000000000000000,即32个零后面跟着1,总共33位。
当它被强制转换为int时,为什么会被视为0?难道不应该将最高位的1作为符号位,并认为这个数字是-2147483648吗?[疑问已解决] 另外,当从Math.pow(4.294967296E9)返回的double直接转换为int时,为什么是2147483647?
我正在阅读一本关于类型转换和数据类型的书,但其中的文本并没有解释得很清楚。我感到困惑。请解释一下为什么第二个和第三个表达式会产生那些结果。
3个回答

3
不行,据我所知,它截断了除第一个32位之外的所有内容,因此它对开头(或者说结尾)的1一无所知。
4294967296 = 0x100000000(十六进制表示),仅取前32位会得到零。
编辑:实际上,我认为另一个问题与从浮点数转换为整数的特殊转换有关,这与经过长整型和整型的转换不同,因为它是一种完全不同的处理器转换。

你说它截断了第一个32位之后的所有内容。但是当写成二进制数时,1在“第一个32位”内部。如果有32个零,然后是1,那么你所说的就是正确的。但是现在只有31个零,然后是1。如果它截断了超过32位,那么1就不应该被截断! - user529141
1
2^32在二进制中是一个后面跟着32个零的1。同样地,10^10在十进制中是一个后面跟着10个零的1。http://coppervinesoftware.com/2to32.png - jdmichal
糟糕!我的错。抱歉 :) 现在我明白了.. :D +1 - user529141
1
0x100000000有八个零,所以是8*4=32个零位。 1是第33位,被舍弃了。 - user541686

2
Java规范的第5.1.3节涵盖了这个问题。 http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25363 整数缩小只取最低的n位,意味着符号不被考虑在内。实际上,负数可能变成正数。扩大没有这个属性,会将数字符号扩展到更宽的类型中。
int value = (int)(long)(Math.pow(2, 31)); // double to long to int
System.out.println(value); // Prints -2147483648

long lvalue = value; // int back to long
System.out.println(value); // Prints -2147483648 again

浮点数缩窄是一种更加复杂的过程,它基本上将数字截断为目标类型中最接近的表示形式,向零舍入。在这种情况下,会触发溢出/下溢规则,分别将float转换为该类型可表示的最大/最小值。


虽然我们应该链接到JLS 3e,但是对于问题的第3部分的详细答案给予+1。http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.3 - robert_x44

0

来自规范,第5.1.3节。T是byte、short、char或int之一。

将有符号整数缩小转换为整数类型T只会丢弃除n个最低位以外的所有位,其中n是用于表示类型T的位数。除了可能丢失有关数字值大小的信息外,这还可能导致结果值的符号与输入值的符号不同。

你最终得到的是低位,这使得此情况变为0。


如果这本书上有那4行代码就好了。整整一页的文字描述除了你从规格中引用的内容以外的一切。感谢您的澄清 :) +1.. 我怀疑符号位会被忽略,但无法确认。 - user529141
你提供的 "spec" 链接是 Java 的最新版本吗? - user529141
1
第一个链接指向最新版本,第二个链接指向以前的版本。但是类型转换没有改变,新的Java版本完全兼容,只添加了一些新的东西,比如泛型。 - Sergei Tachenov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接