如何检查Java中两个数字相乘是否会导致溢出?
-
11-09-2019 - |
题
我想处理两个数字相乘会导致溢出的特殊情况。代码看起来像这样:
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.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 库提供安全算术运算,可检查长溢出/下溢。例如,番石榴的 LongMath.checkedMultiply(长a, 长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
}
更新:该代码是短并且效果很好;然而,它不能用于= -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
}
请注意,这将捕捉一些溢出而没有任何除法。
下面是我能想到的最简单的方法
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
}
选择是两个数字的更大的
我想建立在约翰Kugelman的回答没有通过直接编辑它取代它。它适用于他的测试案例(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
<强>之前downvote 强>: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,您仍然可以“借用”它们非常紧凑的实现。
以下是第 3 方网站上这些实现的一些链接,不能保证这些实现是否保持有效,但无论如何,您应该能够进入 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)