如何在Java中检查两个数字相乘是否会导致溢出?
我想处理两个数字相乘导致溢出的特殊情况。代码如下所示:如何在Java中检查两个数字相乘是否会导致溢出?,java,math,long-integer,integer-overflow,Java,Math,Long Integer,Integer Overflow,我想处理两个数字相乘导致溢出的特殊情况。代码如下所示: int a = 20; long b = 30; // if a or b are big enough, this result will silently overflow long c = a * b; long c; if (a * b will overflow) { c = Long.MAX_VALUE; } else { c = a * b; } 这是一个简化的版本。在实际程序中,a和b在运行时从别处获取
int a = 20;
long b = 30;
// if a or b are big enough, this result will silently overflow
long c = a * b;
long c;
if (a * b will overflow) {
c = Long.MAX_VALUE;
} else {
c = a * b;
}
这是一个简化的版本。在实际程序中,a
和b
在运行时从别处获取。我想要实现的是这样的目标:
int a = 20;
long b = 30;
// if a or b are big enough, this result will silently overflow
long c = a * b;
long c;
if (a * b will overflow) {
c = Long.MAX_VALUE;
} else {
c = a * b;
}
您建议我如何编写此代码
更新:a
和b
在我的场景中始终是非负的。可能:
if(b!= 0 && a * b / b != a) //overflow
不确定这个“解决方案”
编辑:添加了b!=0
在你否决投票之前:a*b/b不会被优化。这可能是编译器错误。我仍然没有发现可以掩盖溢出错误的情况。您可以改用java.math.biginger并检查结果的大小(尚未测试代码):
使用对数检查结果的大小。Java是否有类似int.MaxValue的内容?如果是,请尝试
if (b != 0 && Math.abs(a) > Math.abs(Long.MAX_VALUE / b))
{
// it will overflow
}
编辑:查看有问题的Long.MAX_值如果
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
}
long max=long.signum(a)==long.signum(b)?Long.MAX\u值:Long.MIN\u值;
如果(a!=0&&(b>0&&b>最大值)/a||
b<0&&b
这里有一张小桌子,我用它来检查,假装溢出发生在-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
a=5b=22>10/5
a=2 b=5>10/2
a=-5B=22>-10/-5
a=-2b=55>-10/-2
a=5 b=-2-2<-10/5
a=2 b=-5-5<-10/2
a=-5b=-2-2<10/-5
a=-2b=-5-5<10/-2
我不知道为什么没有人在寻找这样的解决方案:
if (Long.MAX_VALUE/a > b) {
// overflows
}
从两个数字中选择一个更大的。也许这会帮助您:
/**
* @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;
}
}
从jruby偷来的
long result = a * b;
if (a != 0 && result / a != b) {
// overflow
}
更新:此代码很短,运行良好;但是,对于a=-1,b=Long.MIN_值,它会失败
一个可能的增强:
long result = a * b;
if( (Math.signum(a) * Math.signum(b) != Math.signum(result)) ||
(a != 0L && result / a != b)) {
// overflow
}
注意,这将捕获一些没有任何除法的溢出。c/c++(long*long):
java(int*int,很抱歉,我在java中没有找到int64):
1.以大字体保存结果(int*int将结果放入long,long*long放入int64)
2.cmp结果>>位和结果>>(位-1)有一些Java库提供安全的算术运算,用于检查长溢出/下溢。例如,Guava's返回
a
和b
的乘积,如果a*b
在有符号long
算术中溢出,则返回a
和b
,并抛出算术异常。它们在溢出时抛出未经检查的算术异常。我想在John Kugelman的答案的基础上构建它,而不需要直接编辑它来替换它。它适用于他的测试用例(MIN_VALUE=-10
,MAX_VALUE=10
),因为MIN_VALUE==-MAX_VALUE
的对称性,而不是两个补码整数的对称性。实际上,MIN\u VALUE==-MAX\u 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_值
和MAX_值
时,当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
}
long max=long.signum(a)==long.signum(b)?Long.MAX\u值:Long.MIN\u值;
if((a=-1&&b==Long.MIN\u值)||
(a!=-1&&a!=0&&((b>0&&b>最大值/a)||
(b<0&&b
它不是任何MIN_值
和MAX_值
的通用解决方案,但它是Java的Long
和Integer
以及a
和b的任何值的通用解决方案,正如已经指出的那样,Java 8具有Math.xxxExact方法,可以在溢出时抛出异常
如果您的项目没有使用Java8,您仍然可以“借用”它们非常紧凑的实现
下面是JDK源代码存储库中这些实现的一些链接,不能保证这些实现是否有效,但无论如何,您应该能够下载JDK源代码,并查看它们在java.lang.Math
类中如何发挥作用
Math.multiplyExact(long,long)
Math.addExact(long,long)
等等等等
更新:切换到第三方网站的无效链接到开放JDK的Mercurial存储库的链接。这是我能想到的最简单的方法
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"
}
我不回答,但看看Java代码,它很简单。在JDK8中,它转换为长操作,并将结果向下转换为int值,并与长结果进行比较,以查看值是否已更改。下面的代码解释得比我好
@HotSpotIntrinsicCandidate
public static int multiplyExact(int x, int y) {
long r = (long)x * (long)y;
if ((int)r != r) {
throw new ArithmeticException("integer overflow");
}
return (int)r;
}
不过,我觉得这个解决方案可能是最好的办法。我假设这是一个数值应用程序,这就是为什么我不立即推荐它的原因,但这可能是解决这个问题的最佳方法。我不确定您是否可以将“>”运算符与BigInteger一起使用。应该使用compareTo方法。更改为compareTo,速度可能重要,也可能不重要,这取决于使用代码的环境。您的意思是:ceil(log(a))+ceil(log(b))>log(Long.MAX)
?我检查了它。对于较小的值,它比BigInteger快20%,对于接近MAX的值,它几乎相同(快5%)。Yossarian的代码是最快的(比BigInteger快95%和75%)。我怀疑它在某些情况下可能会失败。请记住,整数日志实际上只是计算前导零的数量,您可以优化一些常见情况(例如,如果((a | b)&0xffffffff00000000L)==0)您知道您是安全的)。另一方面,除非你能
@HotSpotIntrinsicCandidate
public static int multiplyExact(int x, int y) {
long r = (long)x * (long)y;
if ((int)r != r) {
throw new ArithmeticException("integer overflow");
}
return (int)r;
}