[java] 왜 int i = 1024 * 1024 * 1024 * 1024가 오류없이 컴파일됩니까?

제한은 int-2147483648에서 2147483647입니다.

입력하면

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의 기본 질문 일 수도 있지만 두 번째 변형이 왜 오류가 발생하지 않는지 모르겠습니다.



답변

그 진술에는 아무런 문제가 없습니다. 4 개의 숫자를 곱하고 int에 할당하면 오버플로가 발생합니다. 이것은 컴파일 타임에 바운드 검사되는 단일 리터럴을 할당하는 것과 다릅니다 .

할당이 아닌 오류를 일으키는 것은 범위를 벗어난 리터럴 입니다 .

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

대조적으로 long리터럴은 잘 컴파일됩니다.

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

사실, 결과는 다음 사항을주의 되어 있기 때문에 여전히 컴파일 시간에 계산 1024 * 1024 * 1024 * 1024A는 상수 표현 :

int i = 1024 * 1024 * 1024 * 1024;

된다 :

   0: iconst_0      
   1: istore_1      

결과 ( 0)는 단순히로드 및 저장되며 곱셈이 발생하지 않습니다.


에서 JLS §3.10.1 (코멘트에서 그것을 양육에 대한 @ChrisK 덕분에) :

유형의 소수 리터럴 int2147483648(2 31 ) 보다 크 거나 소수 리터럴 2147483648이 단항 빼기 연산자의 피연산자 ( §15.15.4 ) 이외의 다른 곳에 나타나는 경우 컴파일 타임 오류 입니다.


답변

1024 * 1024 * 1024 * 1024그리고 2147483648자바에서 같은 값이 없습니다.

사실, 2147483648 심지어 값이 아닌 (비록 2147483648L자바이다). 컴파일러는 말 그대로 그것이 무엇인지 또는 어떻게 사용하는지 모릅니다. 그래서 울린다.

1024Java의 유효한 int이며 유효한 valid int에 다른 valid을 곱한 값 int은 항상 valid int입니다. 계산이 오버플로되기 때문에 직관적으로 기대하는 것과 같은 값이 아닌 경우에도 마찬가지입니다.

다음 코드 샘플을 고려하십시오.

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

이것이 컴파일 오류를 생성 할 것으로 예상됩니까? 이제 조금 더 미끄러 워집니다.
반복을 3 번 반복하고 루프를 곱하면 어떻게 될까요?

컴파일러는 최적화 할 수 있지만 그렇게하는 동안 프로그램의 동작을 변경할 수 없습니다.


이 사건이 실제로 어떻게 처리되는지에 대한 정보 :

Java 및 기타 여러 언어에서 정수는 고정 된 비트 수로 구성됩니다. 주어진 비트 수에 맞지 않는 계산은 오버플로됩니다 . 계산은 기본적으로 Java에서 계수 2 ^ 32로 수행 된 후 값이 부호있는 정수 로 다시 변환됩니다 .

다른 언어 또는 API는 동적 비트 수 ( BigIntegerJava)를 사용하거나 예외를 발생 시키거나 값을 숫자가 아닌 마법의 값으로 설정합니다.


답변

두 번째 변형이 왜 오류가 발생하지 않는지 모르겠습니다.

계산에서 정수에 저장할 수있는 가장 큰 값보다 큰 값을 생성 할 때 진단 메시지를 생성 할 때 제안하는 동작 은 기능 입니다. 기능을 사용하려면 기능을 생각하고, 좋은 아이디어로 간주하고, 설계, 지정, 구현, 테스트, 문서화하여 사용자에게 제공해야합니다.

Java의 경우 해당 목록에있는 항목 중 하나 이상이 발생하지 않았으므로 기능이 없습니다. 나는 어느 것을 모른다; Java 디자이너에게 문의해야합니다.

C #의 경우, 약 14 년 전에 이러한 모든 일이 발생 했으므로 C #의 해당 프로그램은 C # 1.0부터 오류를 생성했습니다.


답변

arshajii의 답변 외에도 한 가지 더 보여주고 싶습니다.

오류를 일으키는 것은 할당 이 아니라 단순히 리터럴을 사용하는 것입니다 . 당신이 시도 할 때

long i = 2147483648;

오른쪽이 여전히 int리터럴이고 범위를 벗어 났기 때문에 컴파일 오류가 발생 합니다.

따라서 int-values ​​(및 할당을 포함하는) 작업은 컴파일 오류없이 (런타임 오류없이) 오버플로 될 수 있지만 컴파일러는 너무 큰 리터럴을 처리 할 수 ​​없습니다.


답변

A : 오류가 아니기 때문입니다.

배경 :1024 * 1024 * 1024 * 1024하면 오버플로가 발생합니다. 오버플로는 종종 버그입니다. 오버플로가 발생하면 프로그래밍 언어에 따라 동작이 달라집니다. 예를 들어, C 및 C ++에서는 부호있는 정수에 대해 “정의되지 않은 동작”이라고하며 동작은 부호없는 정수로 정의됩니다 (수학적 결과를 취하고 UINT_MAX + 1결과가 음수 인 경우 더하고 UINT_MAX + 1결과가을 초과하는 경우 빼기 UINT_MAX).

Java의 경우, int값이 있는 연산의 결과가 허용 범위에없는 경우 개념적으로 Java는 결과가 허용 범위에 올 때까지 2 ^ 32를 더하거나 뺍니다. 따라서 진술은 완전히 합법적이며 오류가 아닙니다. 그것은 단지 당신이 기대했을지도 모르는 결과를 산출하지 않습니다.

이 동작이 도움이되는지 여부와 컴파일러가 경고를 표시해야하는지 확실히 논쟁 할 수 있습니다. 개인적으로 경고가 매우 유용하다고 말했지만 합법적 인 Java이므로 오류가 잘못되었습니다.


답변