왜 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 덕분에) :

유형의 소수 리터럴 int2147483648(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.