program story

Java에서 두 숫자를 곱하면 오버플로가 발생하는지 어떻게 확인할 수 있습니까?

inputbox 2020. 8. 30. 08:15
반응형

Java에서 두 숫자를 곱하면 오버플로가 발생하는지 어떻게 확인할 수 있습니까?


두 숫자를 곱하면 오버플로가 발생하는 특수한 경우를 처리하고 싶습니다. 코드는 다음과 같습니다.

int a = 20;
long b = 30;

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

그것은 단순화 된 버전입니다. 실제 프로그램에서 a그리고 b런타임에 다른 곳에서 소싱됩니다. 내가 달성하고 싶은 것은 다음과 같습니다.

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

이것을 가장 잘 코딩하는 방법은 무엇입니까?

업데이트 : a그리고 b내 시나리오에서 음이 아닌 항상이다.


자바 (8)이 Math.multiplyExact, Math.addExactint 치의과 긴 등. 이것들은 ArithmeticException오버플 로에 체크되지 않은 것을 던집니다 .


경우 ab모두 긍정적 인 당신은 사용할 수 있습니다 :

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 라이브러리가 있습니다. 예를 들어 Guava의 LongMath.checkedMultiply (long a, long b)오버플로되지 않는 경우 a의 곱을 반환하고 부호있는 산술 에서 오버플로 bArithmeticException발생하면 throw 합니다 .a * blong


대신 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
}

Note that this will catch some overflows without any division.


Here is the simplest way I can think of

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"
}

I am not sure why nobody is looking at solution like:

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

Choose a to be larger of the two numbers.


I'd like to build on John Kugelman's answer without replacing it by editing it directly. It works for his test case (MIN_VALUE = -10, MAX_VALUE = 10) because of the symmetry of MIN_VALUE == -MAX_VALUE, which isn't the case for two's complement integers. In actuality, 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)

When applied to the true MIN_VALUE and MAX_VALUE, John Kugelman's answer yields an overflow case when a == -1 and b == anything else (point first raised by Kyle). Here's a way to fix it:

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
}

It's not a general solution for any MIN_VALUE and MAX_VALUE, but it is general for Java's Long and Integer and any value of a and b.


As has been pointed out, Java 8 has Math.xxxExact methods that throw exceptions on overflow.

If you are not using Java 8 for your project, you can still "borrow" their implementations which are pretty compact.

Here are some links to these implementations in the JDK source code repository, no guarantee whether these will stay valid but in any case you should be able to download the JDK source and see how they do their magic inside the java.lang.Math class.

Math.multiplyExact(long, long) http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/java/lang/Math.java#l925

Math.addExact(long, long) http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/java/lang/Math.java#l830

etc, etc.

UPDATED: switched out invalid links to 3rd party website to links to the Mercurial repositories of Open JDK.


Maybe:

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

Not sure about this "solution".

Edit: Added b != 0.

Before you downvote: a * b / b won't be optimized. This would be compiler bug. I do still not see a case where the overflow bug can be masked.


maybe this will help you:

/**
 * @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;
    }
}

c / c ++ (long * long):

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

java (int * int, sorry I didn't find int64 in java):

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.save the result in large type (int*int put the result to long, long*long put to int64)

2.cmp result >> bits and result >> (bits - 1)

참고URL : https://stackoverflow.com/questions/1657834/how-can-i-check-if-multiplying-two-numbers-in-java-will-cause-an-overflow

반응형