为什么 int i = 1024 * 1024 * 1024 * 1024 编译时没有错误?

2022-08-31 07:29:32

的限制是从 -2147483648 到 2147483647。int

如果我输入

int i = 2147483648;

然后 Eclipse 会在“2147483648”下提示一个红色下划线。

但是如果我这样做:

int i = 1024 * 1024 * 1024 * 1024;

它将编译良好。

public class Test {
    public static void main(String[] args) {        

        int i = 2147483648;                   // error
        int j = 1024 * 1024 * 1024 * 1024;    // no error

    }
}

也许这是Java中的一个基本问题,但我不知道为什么第二个变体没有产生错误。


答案 1

这种说法没有错。你只是将4个数字相乘并将其分配给一个int,恰好有一个溢出。这与分配单个文本不同,后者将在编译时进行边界检查。

导致错误的是越界文本,而不是赋值

System.out.println(2147483648);        // error
System.out.println(2147483647 + 1);    // no error

相比之下,文字可以很好地编译:long

System.out.println(2147483648L);       // no error

请注意,实际上,结果仍然是在编译时计算的,因为它是一个常量表达式1024 * 1024 * 1024 * 1024

int i = 1024 * 1024 * 1024 * 1024;

成为:

   0: iconst_0      
   1: istore_1      

请注意,结果 () 只是简单地加载和存储,并且不会发生乘法。0


来自JLS §3.10.1(感谢@ChrisK在评论中提出它):

如果 int 类型的十进制文本大于 2147483648 (231),或者如果十进制文本2147483648作为一元减号运算符 (§15.15.4) 的操作数出现在其他任何位置,则这是编译时错误。


答案 2

1024 * 1024 * 1024 * 1024并且在 Java 中不具有相同的值。2147483648

实际上,在Java中甚至不是一个值(尽管是)。编译器实际上不知道它是什么,也不知道如何使用它。所以它呜呜咽咽。21474836482147483648L

1024是 Java 中的有效 int,而一个有效值乘以另一个 valid ,始终是有效的 。即使它与直观期望的值不同,因为计算将溢出。intintint

请考虑以下代码示例:

public static void main(String[] args) {
    int a = 1024;
    int b = a * a * a * a;
}

你会期望这会产生编译错误吗?现在它变得更加滑溜。
如果我们把一个有3次迭代的循环放在循环中乘法呢?

允许编译器进行优化,但在进行优化时无法更改程序的行为。


有关实际处理此案例的一些信息:

在Java和许多其他语言中,整数将由固定位数组成。不适合给定位数的计算将溢出;计算基本上是在Java中以模数2 ^ 32执行的,之后值被转换回有符号整数。

其他语言或API使用动态位数(在Java中),引发异常或将值设置为幻值,例如not-a-number。BigInteger


推荐