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


152

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


10
컴파일러가 일반적으로 계산을 최적화로서 단일 값으로 "축소"하더라도 최적화가 프로그램의 동작을 변경하지 않아야하므로 결과가 오버플로 인 경우에는 그렇게하지 않습니다.
— Hot Licks

1
그리고 그것은 해석 할 수 없습니다2147483648 :이 문자는 의미가 없습니다.
— Denys Séguret

1
Java는 정수 오버플로를보고하지 않습니다. 작업이 "실패"합니다.
— Hot Licks

5
@JacobKrall : C #은 체크 기능이 켜져 있는지 여부에 관계없이이를 결함으로보고합니다. 상수 표현식만으로 구성된 모든 계산은 확인되지 않은 영역에 있지 않으면 자동으로 확인됩니다.
— Eric Lippert

54
StackOverflow에 대해 "왜"질문을하지 않는 것이 좋습니다. 그들은 대답하기 어렵다. "왜 안 돼요"라는 질문은 세상은 분명히 그렇지 않은 길이어야하고, 그렇게해야 할 충분한 이유가 있어야한다는 것을 전제로합니다. 이 가정은 거의 유효하지 않습니다. 보다 정확한 질문은 "정수 정수 산술 계산 방법을 기술하는 사양의 섹션은 무엇입니까?"와 같은 것입니다. 또는 "정수 오버플로는 Java에서 어떻게 처리됩니까?"
— Eric Lippert

답변:


233

그 진술에는 아무런 문제가 없습니다. 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 덕분에) :

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


12
그리고 곱셈에 대해 JLS에 따르면, 정수 곱셈이 오버플로되면 결과는 충분히 큰 2의 보수 형식으로 표현 된 수학 곱의 하위 비트입니다. 결과적으로 오버 플로우가 발생하면 결과의 부호가 두 피연산자 값의 수학 곱의 부호와 같지 않을 수 있습니다.
— Chris K

3
훌륭한 답변입니다. 어떤 사람들은 오버플로가 일종의 오류나 실패라는 인상을받는 것 같지만 그렇지 않습니다.
— Wouter Lievens

3
@ iowatiger08 언어 의미론은 JVM과 독립적 인 JLS로 요약되어 있습니다 (따라서 어떤 JVM을 사용하든 상관 없습니다).
— arshajii

4
@WouterLievens, 오버플로 는 명백한 오류 조건이 아닌 경우 일반적으로 "비정상적인"조건입니다. 이것은 대부분의 사람들이 수학을 할 때 직관적으로 일어날 것으로 기대하지 않는 유한 정밀도 수학의 결과입니다. 경우에 따라 -1 + 1무해합니다. 그러나 1024^4예상치 못한 결과와는 전혀 다른 예상치 못한 결과를 가진 사람들의 눈을 멀게 할 수 있기 때문입니다. 나는 적어도 사용자에게 경고 나 메모가 있어야한다고 생각하고 그것을 무시하지 마십시오.
— Phil Perry

1
@ iowatiger08 : int의 크기는 고정되어 있습니다. JVM에 의존 하지 않습니다 . 자바는 C 가 아니다
— Martin Schröder

43

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)를 사용하거나 예외를 발생 시키거나 값을 숫자가 아닌 마법의 값으로 설정합니다.


8
나를 위해, 당신의 문 " 2147483648(비록 심지어 값이 아닙니다 2147483648L입니다)," 정말 @arshajii을 만들려고했다는 점을 시멘트.
— kdbanman

아, 미안합니다 귀하의 답변에 개념 오버플로 / 모듈 식 산술이 누락되었습니다. 내 편집 내용에 동의하지 않으면 롤백 할 수 있습니다.
— Maarten Bodewes

@owlstead 당신의 편집은 사실 정확합니다. 포함하지 않는 이유는 : 1024 * 1024 * 1024 * 1024처리 방법에 관계없이 글쓰기와 같지 않다는 점을 강조하고 싶었습니다 2147473648. 언어가 처리 할 수있는 방법에는 여러 가지가 있으며 몇 가지를 나열했습니다. 합리적으로 분리되어 유용합니다. 그래서 나는 그것을 떠날 것이다. 인기있는 질문에 대한 높은 답변을 얻을 때 많은 정보가 점점 더 필요 해지고 있습니다.
— Cruncher

16

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

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

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

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


45
이것은 도움이되는 것을 추가하지 않습니다. Java에서 찌르는 것을 신경 쓰지 않지만 OP 질문에 전혀 대답하지 않았습니다.
— Seiyria

29
@Seiyria : 원래 포스터는 "왜 안돼?" 질문- "왜 세상은 내가 생각하는 방식이 아닌가?" 아닌 실제 코드에 대한 정확한 기술적 인 문제는 ,이 그러므로 StackOverflow에 대한 나쁜 질문입니다. 모호하고 비 기술적 인 질문에 대한 정답이 모호하고 비 기술적이라는 사실은 놀라운 것이 아닙니다. 나는 원래의 포스터가 더 나은 질문을하도록 권장하고 "왜 그렇지 않습니까?" 질문.
— 에릭 리퍼 트

18
@Seiyria : 제가 받아 들인 대답은이 모호하고 기술적이지 않은 질문에도 대답하지 않습니다. 문제는 "왜 이것이 오류가 아닌가?"입니다. 받아 들여지는 대답은 "법적이기 때문"입니다. 이것은 단순히 질문을 되풀이하고있다 . "하늘이 녹색이 ​​아닌 이유는 무엇입니까?" "파란색이기 때문에"라는 질문에 대답하지 않습니다. 그러나 그 질문은 나쁜 질문이므로, 나는 응답자를 전혀 비난하지 않습니다. 대답은 열악한 질문에 대한 완벽한 합리적 대답입니다.
— Eric Lippert

13
에릭 씨, 이것은 제가 게시 한 질문입니다 : "왜 int i = 1024 * 1024 * 1024 * 1024; 일식에 오류보고없이?". 그리고 arshajii의 대답은 정확히 무엇입니까 (아마도 더). 때로는 매우 정확한 방법으로 질문을 표현할 수 없습니다. 그래서 일부 사람들이 Stackoverflow에서 게시 된 질문을보다 정확하게 수정하는 이유가 있다고 생각합니다. "합법적이므로"답변을 원한다면이 질문을 게시하지 않을 것입니다. 나는 "정기적 인 질문들"을 게시하기 위해 최선을 다할 것이지만, 학생이고 전문가가 아닌 나와 같은 사람을 이해하십시오. 감사.
— WUJ

5
@WUJ이 답변은 추가 통찰력과 관점을 제공합니다. 모든 답변을 읽은 후이 답변이 제공된 다른 답변만큼 많은 유효성을 제공한다는 것을 알았습니다. 또한 개발자가 일부 소프트웨어 제품의 유일한 구현자가 아니라는 인식을 높입니다.
— SoftwareCarpenter

12

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

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

long i = 2147483648;

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

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


1
권리. long에 int를 할당하면 암시 적 캐스트가 포함됩니다. 그러나 그 가치는 처음으로
— 던져

4

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

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

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

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.