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

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

有帮助吗?

解决方案

爪哇8具有Math.multiplyExactMath.addExact等,用于整数和长。这些引发选中ArithmeticException上溢出。

其他提示

如果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

有一些 Java 库提供安全算术运算,可检查长溢出/下溢。例如,番石榴的 LongMath.checkedMultiply(长a, 长b) 返回的产品 ab, ,只要它不溢出,并抛出 ArithmeticException 如果 a * b 有符号溢出 long 算术。

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

更新:该代码是短并且效果很好;然而,它不能用于= -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
}

请注意,这将捕捉一些溢出而没有任何除法。

下面是我能想到的最简单的方法

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 (Long.MAX_VALUE/a > b) {
     // overflows
} 

选择是两个数字的更大的

我想建立在约翰Kugelman的回答没有通过直接编辑它取代它。它适用于他的测试案例(MIN_VALUE = -10MAX_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_VALUEMAX_VALUE,约翰Kugelman的回答产生溢出情况下a == -1b ==别的(点首先由凯尔提出)。这里的一个方法来解决它:

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的LongIntegerab的任何值。

也许:

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

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

编辑:!添加B = 0

<强>之前downvote :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,您仍然可以“借用”它们非常紧凑的实现。

以下是第 3 方网站上这些实现的一些链接,不能保证这些实现是否保持有效,但无论如何,您应该能够进入 JDK 源代码并了解它们如何在内部发挥作用。 java.lang.Math 班级。

Math.multiplyExact(long, long) 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

等等等等

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.save结果在大型为(int * INT把结果以长,长×长投入的int64)

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top