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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24676375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 14:02:56  来源:igfitidea点击:

Why does int i = 1024 * 1024 * 1024 * 1024 compile without error?

javaint

提问by WUJ

The limit of intis from -2147483648 to 2147483647.

的限制int是从 -2147483648 到 2147483647。

If I input

如果我输入

int i = 2147483648;

then Eclipse will prompt a red underline under "2147483648".

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

But if I do this:

但如果我这样做:

int i = 1024 * 1024 * 1024 * 1024;

it will compile fine.

它会编译得很好。

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

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

    }
}

Maybe it's a basic question in Java, but I have no idea why the second variant produces no error.

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

采纳答案by arshajii

There's nothing wrong with that statement; you're just multiplying 4 numbers and assigning it to an int, there just happens to be an overflow. This is different than assigning a single literal, which would be bounds-checked at compile-time.

这句话没有错;您只是将 4 个数字相乘并将其分配给一个 int,恰好发生了溢出。这与分配单个文字不同,后者将在编译时进行边界检查。

It is the out-of-bounds literalthat causes the error, not the assignment:

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

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

By contrast a longliteral would compile fine:

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

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

Note that, in fact, the result isstill computed at compile-time because 1024 * 1024 * 1024 * 1024is a constant expression:

需要注意的是,其实,结果仍然在编译时计算的,因为1024 * 1024 * 1024 * 1024是一个常数表达式

int i = 1024 * 1024 * 1024 * 1024;

becomes:

变成:

   0: iconst_0      
   1: istore_1      

Notice that the result (0) is simply loaded and stored, and no multiplication takes place.

请注意,结果 ( 0) 只是简单地加载和存储,没有进行乘法运算。



From JLS §3.10.1(thanks to @ChrisK for bringing it up in the comments):

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

It is a compile-time error if a decimal literal of type intis larger than 2147483648(231), or if the decimal literal 2147483648appears anywhere other than as the operand of the unary minus operator (§15.15.4).

如果类型的十进制文字int大于2147483648(2 31),或者十进制文字2147483648出现在除作为一元减号运算符 ( §15.15.4)的操作数之外的任何地方,则会出现编译时错误。

回答by Eric Lippert

I have no idea why the second variant produces no error.

我不知道为什么第二个变体不会产生错误。

The behaviour that you suggest -- that is, the production of diagnostic message when a computation produces a value that is larger than the largest value that can be stored in an integer-- is a feature. For you to use any feature, the feature must be thought of, considered to be a good idea, designed, specified, implemented, tested, documented and shipped to users.

您建议的行为——即,当计算产生的值大于可以存储在整数中的最大值时产生诊断消息——是一个特性。要使用任何功能,必须考虑该功能,认为该功能是一个好主意,设计、指定、实施、测试、记录并交付给用户。

For Java, one or more of the things on that list did not happen, and therefore you don't have the feature. I don't know which one; you'd have to ask a Java designer.

对于 Java,该列表中的一项或多项事情没有发生,因此您没有该功能。我不知道是哪一个;你得问一个 Java 设计师。

For C#, all of those things did happen -- about fourteen years ago now -- and so the corresponding program in C# has produced an error since C# 1.0.

对于 C#,所有这些事情都确实发生了——大约在 14 年前——所以 C# 中的相应程序自 C# 1.0 以来就产生了错误。

回答by piet.t

In addition to arshajii's answer I want to show one more thing:

除了 arshajii 的回答,我还想再说明一件事:

It is not the assignmentthat causes the error but simply the use of the literal. When you try

导致错误的不是赋值,而只是文字的使用。当你尝试

long i = 2147483648;

you'll notice it also causes a compile-error since the right hand side still is an int-literal and out of range.

您会注意到它还会导致编译错误,因为右侧仍然是int-literal 并且超出范围。

So operations with int-values (and that's including assignments) may overflow without a compile-error (and without a runtime-error as well), but the compiler just can't handle those too-large literals.

因此,带有int-values 的操作(包括赋值)可能会在没有编译错误(也没有运行时错误)的情况下溢出,但编译器无法处理那些过大的文字。

回答by Cruncher

1024 * 1024 * 1024 * 1024and 2147483648do not have the same value in Java.

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

Actually, 2147483648ISN'T EVEN A VALUE(although 2147483648Lis) in Java. The compiler literally does not know what it is, or how to use it. So it whines.

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

1024is a valid int in Java, and a valid intmultiplied by another valid int, is always a valid int. Even if it's not the same value that you would intuitively expect because the calculation will overflow.

1024是 Java 中的有效 int,并且有效int乘以另一个有效int,始终是有效int。即使它与您直觉上期望的值不同,因为计算会溢出。

Example

例子

Consider the following code sample:

考虑以下代码示例:

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

Would you expect this to generate a compile error? It becomes a little more slippery now.
What if we put a loop with 3 iterations and multiplied in the loop?

您是否希望这会产生编译错误?现在变得有点滑了。
如果我们将一个循环进行 3 次迭代并在循环中相乘会怎样?

The compiler is allowed to optimize, but it can't change the behaviour of the program while it's doing so.

允许编译器进行优化,但它不能在执行此操作时更改程序的行为。



Some info on how this case is actually handled:

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

In Java and many other languages, integers will consist of a fixed number of bits. Calculations that don't fit in the given number of bits will overflow; the calculation is basically performed modulus2^32 in Java, after which the value is converted back into a signedinteger.

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

Other languages or API's use a dynamic number of bits (BigIntegerin Java), raise an exception or set the value to a magic value such as not-a-number.

其他语言或 API 使用动态位数(BigInteger在 Java 中)、引发异常或将值设置为魔术值,例如非数字。

回答by gnasher729

A:Because it is not an error.

A:因为这不是错误。

Background:The multiplication 1024 * 1024 * 1024 * 1024will lead to an overflow. An overflow is very often a bug. Different programming languages produce different behavior when overflows happen. For example, C and C++ call it "undefined behavior" for signed integers, and the behavior is defined unsigned integers (take the mathematical result, add UINT_MAX + 1as long as the result is negative, subtract UINT_MAX + 1as long as the result is greater than UINT_MAX).

背景:乘法1024 * 1024 * 1024 * 1024会导致溢出。溢出通常是一个错误。发生溢出时,不同的编程语言会产生不同的行为。例如,C 和 C++ 称其为有符号整数的“未定义行为”,而行为被定义为无符号整数(取数学结果,UINT_MAX + 1只要结果为负就相加UINT_MAX + 1,只要结果大于 就相减UINT_MAX)。

In the case of Java, if the result of an operation with intvalues is not in the allowed range, conceptually Java adds or subtracts 2^32 until the result is in the allowed range. So the statement is completely legal and not in error. It just doesn't produce the result that you may have hoped for.

在 Java 的情况下,如果一个带有int值的操作的结果不在允许的范围内,从概念上 Java 加或减 2^32,直到结果在允许的范围内。所以该声明是完全合法的,没有错误。它只是不会产生您可能希望的结果。

You can surely argue whether this behavior is helpful, and whether the compiler should give you a warning. I'd say personally that a warning would be very useful, but an error would be incorrect since it is legal Java.

您当然可以争论这种行为是否有帮助,以及编译器是否应该给您警告。我个人认为警告会非常有用,但错误是不正确的,因为它是合法的 Java。