112

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

これをコーディングするのに最適な方法をどのように提案しますか?

更新:aそしてb私のシナリオでは常に負ではありません。

4

15 に答える 15

107

Java 8 にはMath.multiplyExactMath.addExactint と long の などがあります。ArithmeticExceptionこれらは未チェックのオーバーフローをスローします。

于 2014-09-24T12:13:01.130 に答える
61

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
于 2009-11-01T18:16:08.220 に答える
18

長いオーバーフロー/アンダーフローをチェックする安全な算術演算を提供するJavaライブラリがあります。たとえば、GuavaのLongMath.checkedMultiply(long a、long b)は、オーバーフローしない場合はaとの積を返し、符号付き算術でオーバーフローした場合はスローします。bArithmeticExceptiona * blong

于 2012-12-05T03:16:35.557 に答える
6

代わりに 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()
}
于 2009-11-01T18:07:36.833 に答える
5

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

于 2009-11-01T18:09:50.543 に答える
4

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

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

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

于 2009-11-01T18:12:22.643 に答える
3

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
}

これにより、除算なしでいくつかのオーバーフローがキャッチされることに注意してください。

于 2011-06-01T00:38:25.823 に答える
3

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

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

JDK ソース コード リポジトリ内のこれらの実装へのリンクをいくつか示します。これらが有効であるかどうかは保証されませんが、いずれにせよ、JDK ソースをダウンロードして、それらがjava.lang.Mathクラス内でどのように機能するかを確認できるはずです。

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

などなど

更新: サード パーティの Web サイトへの無効なリンクを、Open JDK の Mercurial リポジトリへのリンクに切り替えました。

于 2016-10-18T15:14:31.860 に答える
2

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)

真の and に適用するMIN_VALUEMAX_VALUE、ジョン・クーゲルマンの答えは、オーバーフローの場合に発生しa == -1b ==それ以外の場合 (カイルによって最初に提起された点)。これを修正する方法は次のとおりです。

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これはandの一般的な解決策ではありませんMAX_VALUEが、Java のLongandおよびandIntegerの任意の値には一般的です。ab

于 2015-11-03T18:26:24.630 に答える
2

次のような解決策を誰も見ていない理由がわかりません。

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

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

于 2009-11-01T18:58:56.120 に答える
1

多分:

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

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

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

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

于 2009-11-01T18:04:59.257 に答える
1

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

/**
 * @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;
    }
}
于 2009-11-01T21:47:31.530 に答える
-1

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 は結果を long に入れ、long*long は int64 に入れます)

2.cmp 結果 >> ビットと結果 >> (ビット - 1)

于 2012-12-05T02:36:08.067 に答える