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にはMath.multiplyExact
、Math.addExact
など。intおよびlong。これらは、オーバーフロー時に未チェックの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長いオーバーフロー/アンダーフローをチェックする安全な算術演算を提供するライブラリ。たとえば、Guava's 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の場合は失敗します。
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"
}
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 ==
が他の何か(最初に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
のすべての値に一般的です。
誰も次のような解決策を検討していない理由がわかりません:
if (Long.MAX_VALUE/a > b) {
// overflows
}
2つの数値のうち大きい方を選択します。
多分これはあなたを助けるでしょう:
/**
* @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ソースにアクセスし、_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
などなど.
多分:
if(b!= 0 && a * b / b != a) //overflow
この「解決策」についてはわかりません。
編集:b!= 0を追加しました。
ダウン投票する前に:a * b/bは最適化されません。これはコンパイラのバグです。オーバーフローのバグをマスクできるケースはまだ見当たりません。