112

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

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在我的场景中总是非负面的。

4

15 回答 15

107

Java 8 有int 和 longMath.multiplyExactMath.addExact。这些会引发未经检查ArithmeticException的溢出。

于 2014-09-24T12:13:01.130 回答
61

如果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
于 2009-11-01T18:16:08.220 回答
18

有提供安全算术运算的 Java 库,用于检查长溢出/下溢。例如,Guava 的LongMath.checkedMultiply(long a, long b)返回 and 的乘积ab前提是它不会溢出,ArithmeticException如果a * b溢出则在有符号long算术中抛出。

于 2012-12-05T03:16:35.557 回答
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()
}
于 2009-11-01T18:07:36.833 回答
5

使用对数检查结果的大小。

于 2009-11-01T18:09:50.543 回答
5

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

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"
}
于 2017-08-25T02:33:20.667 回答
4

Java 有类似 int.MaxValue 的东西吗?如果是,那么试试

if (b != 0 && Math.abs(a) > Math.abs(Long.MAX_VALUE / b))
{
 // it will overflow
}

编辑:看到有问题的 Long.MAX_VALUE

于 2009-11-01T18:12:22.643 回答
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
}

请注意,这将在没有任何除法的情况下捕获一些溢出。

于 2011-06-01T00:38:25.823 回答
3

正如已经指出的那样,Java 8 具有 Math.xxxExact 方法,可以在溢出时抛出异常。

如果您的项目没有使用 Java 8,您仍然可以“借用”它们非常紧凑的实现。

以下是 JDK 源代码存储库中这些实现的一些链接,不能保证这些实现是否有效,但无论如何您应该能够下载 JDK 源并查看它们如何在java.lang.Math类中发挥作用。

Math.multiplyExact(long, long) http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/java/lang/Math.java#l925

Math.addExact(long, long) http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/java/lang/Math.java#l830

等等等等

更新:将指向第 3 方网站的无效链接切换到指向 Open JDK 的 Mercurial 存储库的链接。

于 2016-10-18T15:14:31.860 回答
2

我不知道为什么没有人在看像这样的解决方案:

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

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

于 2009-11-01T18:58:56.120 回答
2

我想建立在 John Kugelman 的答案之上,而不是通过直接编辑来替换它。由于 的对称性,它适用于他的测试用例 ( MIN_VALUE = -10, ) ,而二进制补码整数则不然。实际上,.MAX_VALUE = 10MIN_VALUE == -MAX_VALUEMIN_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_VALUEandMAX_VALUE时,John Kugelman 的答案会产生一个溢出情况 whena == -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_VALUEand的通用解决方案MAX_VALUE,但它对于 Java 的LongandInteger以及 and 的任何值都是通用ab

于 2015-11-03T18:26:24.630 回答
1

也许:

if(b!= 0 && a * b / b != a) //overflow

不确定这个“解决方案”。

编辑:添加 b != 0。

在你投反对票之前:a * b / b 不会被优化。这将是编译器错误。我仍然没有看到可以掩盖溢出错误的情况。

于 2009-11-01T18:04:59.257 回答
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;
    }
}
于 2009-11-01T21:47:31.530 回答
1

我不回答,但是看Java的代码,很简单。在JDK8中,它转换为long运算,并将结果向下转换为int值,并与long结果进行比较,看值是否发生了变化。下面的代码比我解释得更好。

@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;
}
于 2021-01-15T08:39:32.287 回答
-1

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.将结果保存为大类型(int*int将结果放入long,long*long放入int64)

2.cmp 结果 >> 位和结果 >> (位 - 1)

于 2012-12-05T02:36:08.067 回答