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

时间:2009-11-01 18:02:41

标签: java overflow math long-integer

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

int a = 20;
long b = 30;

// if a or b are big enough, this result will silently overflow
long c = a * b;

这是一个简化版本。在实际程序中ab在运行时在其他地方获取。我想要实现的是这样的:

long c;
if (a * b will overflow) {
    c = Long.MAX_VALUE;
} else {
    c = a * b;
}

您如何建议我最好编码?

更新:ab在我的方案中始终是非负面的。

14 个答案:

答案 0 :(得分:80)

Java 8有Math.multiplyExactMath.addExact等,用于整数和长整数。这些会在溢出时抛出未经检查的ArithmeticException

答案 1 :(得分:59)

如果ab都是正数,那么您可以使用:

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)返回ab的产品,只要它不溢出,如果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 = 10MIN_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_VALUEMAX_VALUE时,John Kugelman的回答会在a == -1b ==任何其他内容(由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_VALUEMAX_VALUE的通用解决方案,但它适用于Java的LongInteger以及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)