如何检查Java中的两个数字相乘是否会导致溢出?

我想处理两个数字相乘导致溢出的特殊情况。 代码看起来像这样:

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在我的方案中始终是非负的。


对于int和long,Java 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库提供安全的算术运算,可检查长溢出/下溢。 例如,Guava的LongMath.checkedMultiply(long a,long b)返回a和b的乘积,前提是它不溢出,如果a * b在带符号的long算术中溢出,则抛出ArithmeticException。


您可以使用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。

一个可能的增强:

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

请注意,这将捕获一些溢出而不进行任何除法。


我不确定为什么没有人在寻找解决方案:

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

选择一个更大的两个数字。


我希望以John Kugelman的答案为基础,而不是通过直接编辑来替换它。 它适用于他的测试用例(MIN_VALUE,MAX_VALUE),因为Long的对称性,这不是两个补码整数的情况。 实际上,Integer。

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的答案会在Long和Integer的任何其他情况下产生溢出情况(由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。


这是我能想到的最简单的方法

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(b!= 0 && a * b / b != a) //overflow

不确定这个&#34;解决方案&#34;。

编辑:添加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用于您的项目,您仍然可以&#34;借用&#34; 他们的实现非常紧凑。

以下是第三方网站上这些实现的一些链接,不保证这些实现是否保持有效,但无论如何您应该能够进入JDK源并看看他们如何在Math.addExact类中实现他们的魔力。

Math.addExact[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]

等等

StFS answered 2019-08-26T05:23:38Z
-1 votes
c / c ++(long * long):
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结果&gt;&gt; 位和结果&gt;&gt; (位 - 1)