하는지 어떻게 확인할 수 있 곱하여 두 번호 Java 는 원인이 될 것이는 오버플로우?

StackOverflow https://stackoverflow.com/questions/1657834

문제

내가 처리하는 특별한 경우를 곱하여 두 번호 함께 일으키는 오버플로우가 발생합니다.코드는 다음과 같이 나타납니다.

int a = 20;
long b = 30;

// if a or b are big enough, this result will silently overflow
long c = a * b;

는 간소화된 버전입니다.에서 실제 프로그램 ab 공급 다른 곳에 있습니다.내가 무엇을 달성하고 싶은 뭔가가 다음과 같다:

long c;
if (a * b will overflow) {
    c = Long.MAX_VALUE;
} else {
    c = a * b;
}

어떻게 당신이 제안하는 내가 최고의 코드를 이?

업데이트: ab 는 항상 음수가 아닌 내 시나리오에.

도움이 되었습니까?

해결책

Java 8이 있습니다 Math.multiplyExact, Math.addExact int와 긴. 이것들은 확인되지 않은 것을 던졌습니다 ArithmeticException 오버플로.

다른 팁

만약에 a 그리고 b 둘 다 긍정적이므로 사용할 수 있습니다.

if (a != 0 && b > Long.MAX_VALUE / a) {
    // Overflow
}

양수와 음수를 모두 처리 해야하는 경우 더 복잡합니다.

long maximum = Long.signum(a) == Long.signum(b) ? Long.MAX_VALUE : Long.MIN_VALUE;

if (a != 0 && (b > 0 && b > maximum / a ||
               b < 0 && b < maximum / a))
{
    // Overflow
}

여기에 오버플로가 -10 또는 +10에서 발생하는 척하는 척하는 척하는 작은 테이블이 있습니다.

a =  5   b =  2     2 >  10 /  5
a =  2   b =  5     5 >  10 /  2
a = -5   b =  2     2 > -10 / -5
a = -2   b =  5     5 > -10 / -2
a =  5   b = -2    -2 < -10 /  5
a =  2   b = -5    -5 < -10 /  2
a = -5   b = -2    -2 <  10 / -5
a = -2   b = -5    -5 <  10 / -2

안전한 산술 작업을 제공하는 Java 라이브러리가 있으며 오버플로/언더 플로우를 확인합니다. 예를 들어, 구아바 longmath.checkedmultiply (Long A, Long B) 제품을 반환합니다 a 그리고 b, 그것이 오버플로되지 않고 던지면 ArithmeticException 만약에 a * b 서명 된 오버플로 long 산수.

대신 java.math.biginteger를 사용하고 결과의 크기를 확인할 수 있습니다 (코드를 테스트하지 않음).

BigInteger bigC = BigInteger.valueOf(a) * multiply(BigInteger.valueOf(b));
if(bigC.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
  c = Long.MAX_VALUE;
} else {
  c = bigC.longValue()
}

사용 로그를 확인하는 크기의 결과입니다.

Java에는 int.maxvalue와 같은 것이 있습니까? 그렇다면 시도하십시오

if (b != 0 && Math.abs(a) > Math.abs(Long.MAX_VALUE / b))
{
 // it will overflow
}

편집 : long.max_value 문제

Jruby에서 도난당했습니다

    long result = a * b;
    if (a != 0 && result / a != b) {
       // overflow
    }

업데이트 :이 코드는 짧고 잘 작동합니다. 그러나 a = -1, b = long.min_value에는 실패합니다.

하나의 가능한 향상 :

long result = a * b;
if( (Math.signum(a) * Math.signum(b) != Math.signum(result)) || 
    (a != 0L && result / a != b)) {
    // overflow
}

이것은 분열없이 오버플로를 잡을 것입니다.

여기에 내가 생각할 수있는 가장 간단한 방법이 있습니다

int a = 20;
long b = 30;
long c = a * b;

if(c / b == a) {
   // Everything fine.....no overflow
} else {
   // Overflow case, because in case of overflow "c/b" can't equal "a"
}

왜 아무도 다음과 같은 솔루션을보고 있는지 잘 모르겠습니다.

if (Long.MAX_VALUE/a > b) {
     // overflows
} 

두 숫자 중 더 큰 A를 선택하십시오.

John Kugelman의 답변을 직접 편집하여 대체하지 않고 바탕으로 구축하고 싶습니다. 그것은 그의 테스트 사례에서 작동합니다 (MIN_VALUE = -10, MAX_VALUE = 10)의 대칭 때문에 MIN_VALUE == -MAX_VALUE, 이것은 2의 보완 정수의 경우가 아닙니다. 실제로, MIN_VALUE == -MAX_VALUE - 1.

scala> (java.lang.Integer.MIN_VALUE, java.lang.Integer.MAX_VALUE)
res0: (Int, Int) = (-2147483648,2147483647)

scala> (java.lang.Long.MIN_VALUE, java.lang.Long.MAX_VALUE)
res1: (Long, Long) = (-9223372036854775808,9223372036854775807)

참에 적용될 때 MIN_VALUE 그리고 MAX_VALUE, John Kugelman의 답변은 오버 플로우 케이스를 생성합니다. a == -1 그리고 b == 다른 것 (카일에 의해 처음 자란 점). 다음은이를 해결하는 방법입니다.

long maximum = Long.signum(a) == Long.signum(b) ? Long.MAX_VALUE : Long.MIN_VALUE;

if ((a == -1 && b == Long.MIN_VALUE) ||
    (a != -1 && a != 0 && ((b > 0 && b > maximum / a) ||
                           (b < 0 && b < maximum / a))))
{
    // Overflow
}

그것은 어떤 일반적인 해결책이 아닙니다 MIN_VALUE 그리고 MAX_VALUE, 그러나 그것은 Java 's에게는 일반적입니다 Long 그리고 Integer 그리고 모든 가치 a 그리고 b.

아마도:

if(b!= 0 && a * b / b != a) //overflow

이 "솔루션"에 대해 잘 모르겠습니다.

편집 : 추가 b! = 0.

당신이 내리기 전에: A * B / B는 최적화되지 않습니다. 이것은 컴파일러 버그입니다. 오버플로 버그를 가릴 수있는 경우가 여전히 보이지 않습니다.

어쩌면 이것은 당신에게 도움이 될 것입니다 :

/**
 * @throws ArithmeticException on integer overflow
 */
static long multiply(long a, long b) {
    double c = (double) a * b;
    long d = a * b;

    if ((long) c != d) {
        throw new ArithmeticException("int overflow");
    } else {
        return d;
    }
}

지적한 바와 같이, Java 8에는 Math.xxxexact 메소드가 오버플로에 예외를 제외합니다.

프로젝트에 Java 8을 사용하지 않는 경우 여전히 꽤 컴팩트 한 구현을 "빌려"할 수 있습니다.

다음은 제 3 자 웹 사이트에서 이러한 구현에 대한 링크입니다. 이들은 유효한 상태를 유지하는지 여부를 보장하지 않지만 어쨌든 JDK 소스로 들어가서 그들이 어떻게 마술을 어떻게하는지 확인할 수 있어야합니다. java.lang.Math 수업.

Math.multiplyExact(long, long) http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/math.java?av=f#882

Math.addExact http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/math.java?av=f#805

등 등

C / C ++ (긴 * 긴) :

const int64_ w = (int64_) a * (int64_) b;    
if ((long) (w >> sizeof(long) * 8) != (long) w >> (sizeof(long) * 8 - 1))
    // overflow

java (int * int, java에서 int64를 찾지 못했습니다) :

const long w = (long) a * (long) b;    
int bits = 32; // int is 32bits in java    
if ( (int) (w >> bits) != (int) (w >> (bits - 1))) {
   // overflow
}

1. 결과를 큰 유형으로 구하십시오 (int*int 결과를 길고 긴*긴*int64에 넣습니다)

2.CMP 결과 >> 비트 및 결과 >> (비트 -1)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top