Java中原始整数类型的不一致行为

时间:2013-12-06 22:31:52

标签: java primitive-types scjp

有人可以向我解释,因为我为什么会在Java中为四个表示整数的原始类型中的两个获得不同的行为? AFAIK全部四个都是签名的,它们都使用最重要的位作为符号位,那么为什么字节和短行为正常,而int和long行为,好吧,奇怪? oracle文档的片段解释这将是完美的。

byte a = (byte) (Math.pow(2, 7)-1); //127 - as expected
short b = (short) (Math.pow(2, 15)-1); //32767 - as expected
int c = (int) (Math.pow(2, 31)-1); //2147483647 - as expected
long d = (long) (Math.pow(2, 63)-1); //9223372036854775807 - as expected

a = (byte) (Math.pow(2, 7)); //-128 - as expected
b = (short) (Math.pow(2, 15)); //-32768 - as expected
c = (int) (Math.pow(2, 31)); //2147483647 - why not '-2147483648'?
d = (long) (Math.pow(2, 63)); //9223372036854775807 - why not '-9223372036854775808'?

a = (byte) (Math.pow(2, 8)); //0 - as expected
b = (short) (Math.pow(2, 16)); //0 - as expected
c = (int) (Math.pow(2, 32)); //2147483647 - why not '0'?
d = (long) (Math.pow(2, 64)); //9223372036854775807 - why not '0'?

我正在使用Oracle的Java SE 1.7 for Windows。操作系统是Windows 7 Professional SP1

java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)

在阅读完所有答案并调整我的代码后编辑。
总而言之,我发现获得预期值的唯一方法是使用BigInteger。 Shift操作符适用于字节,短路和整数,但是当涉及到多头时,我会在一次故障时使用它。

byte a = (byte) ((1l << 7) - 1); //127 - as expected
short b = (short) ((1l << 15) - 1); //32767 - as expected
int c = (int) (1l << 31) - 1; //2147483647 - as expected
long d = (1l << 63) - 1; //9223372036854775807 - as expected

a = (byte) (1l << 7); //-128 - as expected
b = (short) (1l << 15); //-32768 - as expected
c = (int) 1l << 31; //-2147483648 - as expected
d = 1l << 63; //-9223372036854775808 - as expected

a = (byte) (1l << 8); //0 - as expected
b = (short) (1l << 16); //0 - as expected
c = (int) (1l << 32); //0 - as expected
d = 1l << 64; //1 instead of 0, probably because of the word length limitation      

使用BigInteger,一切都可以正常运行

byte a = (byte) (new BigInteger("2").pow(7).longValue() - 1); //127 - as expected
short b = (short) (new BigInteger("2").pow(15).longValue() - 1); //32767 - as expected
int c = (int) (new BigInteger("2").pow(31).longValue() - 1); //2147483647 - as expected
long d = (new BigInteger("2").pow(63).longValue() - 1); //9223372036854775807 - as expected

a = (byte) (new BigInteger("2").pow(7).longValue()); //-128 - as expected
b = (short) (new BigInteger("2").pow(15).longValue()); //-32768 - as expected
c = (int) new BigInteger("2").pow(31).longValue(); //-2147483648 - as expected
d = new BigInteger("2").pow(63).longValue(); //-9223372036854775808 - as expected

a = (byte) (new BigInteger("2").pow(8).longValue()); //0 - as expected
b = (short) (new BigInteger("2").pow(16).longValue()); //0 - as expected
c = (int) (new BigInteger("2").pow(32).longValue()); //0 - as expected
d = new BigInteger("2").pow(64).longValue(); //0 - as expected

感谢大家的大力帮助!

3 个答案:

答案 0 :(得分:6)

Section 5.1.3 of the JLS讨论了演员使用的缩小基元转换的行为

  

否则,以下两种情况之一必须为真:

     

该值必须太小(大幅度或负无穷大的负值),第一步的结果是int或long类型的最小可表示值。

     

值必须太大(大幅度或正无穷大的正值),第一步的结果是 int或long 类型的最大可表示值。

(强调我的)

这就是(int) (Math.pow(2, 32));成为Integer.MAX_VALUE(long) (Math.pow(2, 64))成为Long.MAX_VALUE的原因。

答案 1 :(得分:3)

Math.pow()会返回double,然后在转换为整数类型时进行舍入。 double显然是在使它们溢出所需的精度下弄圆了头发。

答案 2 :(得分:2)

指导:

public class PowTest {
    public static void main(String[] argv) {
        double powResult = Math.pow(2.0,31.0);
        int powInt = (int) powResult;
        long powLong = (long) powResult;
        int longInt = (int) powLong;
        System.out.println("Double = " + powResult + ", int = " + powInt + ", long = " + powLong + ", longInt = " + longInt);
    }
}

结果:

C:\JavaTools>java PowTest
Double = 2.147483648E9, int = 2147483647, long = 2147483648, longInt = -2147483648

双 - &gt; int转换是四舍五入的。长 - &gt; int转换被截断。