2012-09-01 7 views
24

가능한 중복 :
How can I check if multiplying two numbers in Java will cause an overflow?Java 코드에서 정수 오버플로를 방지하는 방법은 무엇입니까?

한다고 가정 내가 *+ 작업을 사용하는 Java 클래스 메소드를 가지고있다.

 
int foo(int a, int b) { 
    ... // some calculations with + and * 
} 

foo에 오버플로가 발생하지 않도록하는 방법은 무엇입니까?

 
int sum(int a, int b) { 
    int c = a + b; 
    if (a > 0 && b > 0 && c < 0) 
    throw new MyOverfowException(a, b) 
    return c; 
} 

int prod(int a, int b) { 
    int c = a * b; 
    if (a > 0 && b > 0 && c < 0) 
    throw new MyOverfowException(a, b) 
    return c; 
} 

int 오버 플로우가 자바 메소드에서 발생하지 있는지 확인하기 위해 더 나은 방법이 있습니다

은 내가 BigDecimal를 사용하거나 모든 +와 * 같은 "래퍼"로 교체하거나 추측?

+3

왜 0 미만인지 확인합니까? 정수는 0보다 훨씬 작을 수 있습니다. – 11684

+0

정확히 말하면 int의 범위는 -2,147,483,648에서 2,147,483,647입니다. – 11684

+0

값을 고정하려고합니까? –

답변

17

엔지니어링 관점에서는 어려운 문제입니다.

Secure Coding 사이트는 권장 전제 조건의

  • 사용; 오버플로가 불가능하도록 입력을 범위 검사하여 다음 큰 기본 정수 유형을 사용하여 각 개별 산술 연산을 수행하고 명시 적으로 오버플로를 확인하거나 BigInteger를 사용하여
  • 을 사용하여 오버플로를 불가능하게합니다.

Dr Dobbs article은 명시 적 오버플로 검사로 각 기본 연산을 수행하는 기본 산술 방법의 라이브러리를 만드는 것이 좋습니다. (위의 글 머리 기호 2의 구현으로 볼 수 있습니다.)하지만 저자는 바이트 코드 재 작성을 사용하여 산술 바이트 코드를 오버플로 검사가 포함 된 해당 메소드에 대한 호출로 대체하도록 제안함으로써 더 나아갑니다.

불행히도 Java에서 오버플로 검사를 기본적으로 사용할 수있는 방법은 없습니다.(그러나 다른 많은 언어에서도 마찬가지다. 예 : C, C++ ...)

+0

그 중 두 번째는 내 대답이 무엇입니까 – Alnitak

+1

"엔지니어링 관점에서 보면 어려운 문제입니다." - 그다지 어렵지 않습니다. 모든 연산 후에 오버플로 레지스터 플래그를 확인하기위한 기계 코드를 생성하십시오. 그것이 C#의 "checked"블록이하는 것입니다. 문제는 자바가 옵션으로 제공하지 않는다는 것입니다. 왜냐하면 자바는 사람의 재치를 뛰어 넘기 때문이 아닙니다. – Rich

+0

@Rich - Java 컴파일러를 수정할 위치에 있지 않으면 어렵습니다. 대부분의 사람들은 그렇지 않습니다! –

5

합계 : b가 int에 저장할 수있는 최대 값과 a의 값을 뺀 값보다 큰지 확인합니다. a 및/또는 b가 음수 일 수있는 경우, (i) 차액 확인을 위해 이미 오버플로가 발생하지 않도록주의하고 (ii) 최소 금액에 대해 유사한 점검을 수행해야합니다.

제품 : 그게 더 어렵습니다. 정수를 두 개의 절반 길이 정수로 나눕니다 (즉, int가 32 비트이면 비트 마스킹 및 시프 팅을 사용하여 두 개의 16 비트 숫자로 나눕니다). 그런 다음 곱셈을 수행 한 다음 결과가 32 비트에 맞는지 봅니다.

임시 결과로 간단히 long을 사용하고 싶지 않은 모든 조건.

20

오버플로를 확인하는 한 가지 방법은 피연산자를 더 큰 유형 (원래 피연산자 비트 길이의 두 배)으로 승격 한 다음 작업을 수행 한 다음 결과 값이 원래 유형에 비해 너무 큰지 확인하는 것입니다. 예 원래 유형이 long 경우

int sum(int a, int b) { 
    long r = (long)a + b; 
    if (r >>> 32 != 0) { // no sign extension 
     throw new MyOverflowException(a, b); 
    } 
    return (int)r; 
} 

, 당신은 더 큰 유형으로 BigInteger를 사용해야 할 것입니다.

+0

이 답변은 오래되었지만 더 큰 유형 자체가 오버플로되어 작은 유형에 대해 정상적인 범위로 끝날 수 있기 때문에이 해결책이 작동하지 않을 수 있음을 알고 있습니다. – yitzih

+0

@yitzih 잘못된 것입니다. 두 개의 (양수) 정수를 더하면 가장 긴 피연산자보다 1 비트 이상 긴 값을 초과 할 수 없습니다. "더 큰 타입 자체가 오버플로"로 끝날 수있는 질문의 제약 조건이 주어지지 않습니다. – Alnitak

+0

@yitzihI 곱셈 또한 포함된다는 것을 잊었지만, 두 개의 31 비트 양의 정수의 곱은 62 비트를 초과 할 수 없습니다. – Alnitak

3

a와 b가 모두 양수이거나 음수이고 a + b의 부호가 a의 부호와 같지 않고 b이면 오버플로가 발생합니다. 이 규칙을 사용하여 오버플로가 발생하는지 판단하고 예외를 throw 할 수 있습니다. 이 대담을 보게되면, 이전의 대답에서 제시된 방법에 따라 대처할 수 있습니다. 또 다른 방법은 오버플로하지 않는 가장 큰 범위 유형을 사용하여 작업을 수행하는 것입니다. Integer 사이의 연산에 오래 사용할 수 있습니다.

관련 문제