Java で 2 つの数値を乗算するとオーバーフローが発生するかどうかを確認するにはどうすればよいですか?
-
11-09-2019 - |
質問
2 つの数値を乗算するとオーバーフローが発生するという特殊なケースを処理したいと考えています。コードは次のようになります。
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
私のシナリオでは常に負ではありません。
解決
のJava 8は、int型と長いためMath.multiplyExact
、Math.addExact
などがあります。これらは、オーバーフローのチェックを外す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ライブラリがあります。例えば、グアバの<のhref =「https://google.github.io/guava/releases/snapshot-jre/api/docs/com/google/common/math/LongMath.html#checkedMultiply-long-long-」 a
とb
の積を返しのREL = "nofollowをnoreferrer"> LongMath.checkedMultiplyは(長い、長い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のようなものを持っていますか? yesの場合は、してみてください。
if (b != 0 && Math.abs(a) > Math.abs(Long.MAX_VALUE / b))
{
// it will overflow
}
編集:質問にはLong.MAX_VALUEを見て
ジュルビーから盗まれた
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
}
これにより、除算なしで一部のオーバーフローが検出されることに注意してください。
ここで私は考えることができる最も簡単な方法です。
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
}
2つの数の大きくなるように選択します。
私はそれを直接編集して、それを交換することなく、ジョン・Kugelmanの答えに構築したいと思います。これは、理由は2の補数整数の場合ではないMIN_VALUE = -10
の対称性、彼のテストケース(MAX_VALUE = 10
、MIN_VALUE == -MAX_VALUE
)のために働きます。実際には、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
に適用すると、は、ジョン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の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 を使用していない場合でも、非常にコンパクトな実装を「借用」できます。
ここでは、サードパーティ Web サイト上のこれらの実装へのリンクをいくつか示します。これらが有効であり続けるかどうかは保証されませんが、いずれにしても、JDK ソースにアクセスして、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.save(int型* int型*長いint64型に入れ、長い、長いに結果を置く)
2.cmp結果>>ビットと結果>>(ビット - 1)