我想处理两个数字相乘会导致溢出的特殊情况。代码看起来像这样:
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
在我的方案中始终是非负面的。
答案 0 :(得分:80)
Java 8有Math.multiplyExact
,Math.addExact
等,用于整数和长整数。这些会在溢出时抛出未经检查的ArithmeticException
。
答案 1 :(得分:59)
如果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
答案 2 :(得分:17)
有些Java库提供安全的算术运算,可检查长溢出/下溢。例如,Guava的LongMath.checkedMultiply(long a, long b)返回a
和b
的产品,只要它不溢出,如果ArithmeticException
溢出签名a * b
,则会抛出long
算术。
答案 3 :(得分: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()
}
答案 4 :(得分:6)
使用对数来检查结果的大小。
答案 5 :(得分:4)
Java有类似int.MaxValue的东西吗?如果是,请尝试
if (b != 0 && Math.abs(a) > Math.abs(Long.MAX_VALUE / b))
{
// it will overflow
}
编辑:看到有问题的Long.MAX_VALUE
答案 6 :(得分:3)
从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
}
请注意,这会在没有任何除法的情况下捕获一些溢出。
答案 7 :(得分:2)
我不确定为什么没有人在寻找解决方案:
if (Long.MAX_VALUE/a > b) {
// overflows
}
选择一个更大的两个数字。
答案 8 :(得分:2)
我希望以John 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
时,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
和{的任何值{1}}。
答案 9 :(得分:2)
这是我能想到的最简单的方法
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"
}
答案 10 :(得分:1)
也许:
if(b!= 0 && a * b / b != a) //overflow
不确定这个“解决方案”。
编辑:添加了b!= 0。
在你投票之前:a * b / b将不会被优化。这将是编译器错误。我仍然没有看到可以掩盖溢出错误的情况。
答案 11 :(得分: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;
}
}
答案 12 :(得分:1)
正如已经指出的那样,Java 8具有Math.xxxExact方法,可以在溢出时抛出异常。
如果您没有将Java 8用于您的项目,您仍然可以“借用”他们非常紧凑的实现。
以下是第三方网站上这些实现的一些链接,不保证这些实现是否保持有效,但无论如何,您应该能够进入JDK源并看看他们如何在{{1}内部实现他们的魔力} class。
java.lang.Math
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Math.java?av=f#882
Math.multiplyExact(long, long)
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Math.java?av=f#805
等等。
答案 13 :(得分:-1)
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)