简体   繁体   English

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

[英]Inconsistent behaviour of primitive integer types in Java

Can someone explain to me like I'm five why I get different behaviour for two of four primitive types representing integers in Java? 有人可以解释一下,为什么我在Java中为表示整数的四种基本类型中的两种获得不同的行为? AFAIK all four are signed and they all use the most significant bit as a sign bit, so why do byte and short behave normally, and int and long act, well, strange? AFAIK全部四个都是签名的,它们都使用最重要的位作为符号位,那么为什么字节和短行为正常,而int和long行为,好吧,奇怪? The fragment of oracle docs explaining this would be perfect. 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'?

I'm using Oracle's Java SE 1.7 for Windows. 我正在使用Oracle的Java SE 1.7 for Windows。 OS is Windows 7 Professional SP1 操作系统是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)

EDIT, after reading all the answers and tuning my code. 在阅读完所有答案并调整我的代码后编辑。
So, to sum up, the only way I found to get expected values is the use of BigInteger. 总而言之,我发现获得预期值的唯一方法是使用BigInteger。 Shift operator works well for bytes, shorts and ints, but when it comes to longs, I cought it on one malfunction. Shift运算符适用于字节,短路和整数,但是当涉及到longs时,我会在一次故障时使用它。

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      

With BigInteger everything works flawlessly 使用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

Thanks everyone for big help! 谢谢大家的帮助!

Section 5.1.3 of the JLS talks about the behavior of the narrowing primitive conversion used by the cast JLS的5.1.3节讨论了演员使用的缩小基元转换的行为

Otherwise, one of the following two cases must be true: 否则,以下两种情况之一必须为真:

The value must be too small (a negative value of large magnitude or negative infinity), and the result of the first step is the smallest representable value of type int or long. 该值必须太小(大幅度或负无穷大的负值),第一步的结果是int或long类型的最小可表示值。

The value must be too large (a positive value of large magnitude or positive infinity), and the result of the first step is the largest representable value of type int or long . 该值必须太大(大幅度或正无穷大的正值),第一步的结果是int或long类型的最大可表示值

(emphasis mine) (强调我的)

That is why (int) (Math.pow(2, 32)); 这就是为什么(int) (Math.pow(2, 32)); becomes Integer.MAX_VALUE and (long) (Math.pow(2, 64)) becomes Long.MAX_VALUE . 变为Integer.MAX_VALUE(long) (Math.pow(2, 64)) Long.MAX_VALUE (long) (Math.pow(2, 64))变为Long.MAX_VALUE

Math.pow() returns a double , which is then rounded on conversion to the integral types. Math.pow()返回一个double ,然后在转换为整数类型时进行舍入。 The double is evidently rounded a hair under the precision needed to make them overflow. 在使它们溢出所需的精度下, double显然是圆形的。

Instructive: 指导:

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);
    }
}

Result: 结果:

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

The double -> int conversion is rounded. double - > int转换是四舍五入的。 The long -> int conversion is truncated. long - > int转换被截断。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM