web-dev-qa-db-ja.com

Javaで2つの数値を乗算するとオーバーフローが発生するかどうかを確認するにはどうすればよいですか?

2つの数値を乗算するとオーバーフローが発生する特殊なケースを処理したいと思います。コードは次のようになります。

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は、私のシナリオでは常に負ではありません。

92
Steve McLeod

Java 8にはMath.multiplyExactMath.addExactなど。intおよびlong。これらは、オーバーフロー時に未チェックのArithmeticExceptionをスローします。

80
bcoughlan

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
59
John Kugelman

Java長いオーバーフロー/アンダーフローをチェックする安全な算術演算を提供するライブラリ。たとえば、Guava's LongMath.checkedMultiply(long a、long b) は製品を返します。 aおよびbのうち、オーバーフローしない限り、ArithmeticExceptionがスローされる場合、a * b符号付きlong算術演算でオーバーフローします。

17
reprogrammer

代わりに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()
}
6
Ulf Lindback

対数を使用して、結果のサイズを確認します。

Javaにはint.MaxValueのようなものがありますか?はいの場合、試してください

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

編集:問題のLong.MAX_VALUEを見た

4
nothrow

Jrubyから盗まれた

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

更新:このコードは短く、うまく機能します。ただし、a = -1、b = Long.MIN_VALUEの場合は失敗します。

1つの可能な強化:

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

これにより、分割なしでオーバーフローが発生することに注意してください。

3
rogerdpack

ここに私が考えることができる最も簡単な方法があります

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

John Kugelmanの答えを直接編集して置き換えずに構築したいと思います。 MIN_VALUE = -10の対称性のために、彼のテストケース(MAX_VALUE = 10MIN_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 ==が他の何か(最初にKyleによって発生したポイント)の場合にオーバーフローのケースになります。これを修正する方法は次のとおりです。

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のLongおよびIntegerおよびaおよびbのすべての値に一般的です。

2
Jim Pivarski

誰も次のような解決策を検討していない理由がわかりません:

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

2つの数値のうち大きい方を選択します。

2
fastcodejava

多分これはあなたを助けるでしょう:

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

指摘されているように、Java 8には、オーバーフロー時に例外をスローするMath.xxxExactメソッドがあります。

プロジェクトにJava 8を使用していない場合でも、かなりコンパクトな実装を「借りる」ことができます。

サードパーティのWebサイトにあるこれらの実装へのリンクを次に示します。これらが有効なままであるかどうかの保証はありませんが、いずれにしても、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

などなど.

1
StFS

多分:

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

この「解決策」についてはわかりません。

編集:b!= 0を追加しました。

ダウン投票する前に:a * b/bは最適化されません。これはコンパイラのバグです。オーバーフローのバグをマスクできるケースはまだ見当たりません。

1
Thomas Jung