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

qkf9rpyu  于 2021-07-05  发布在  Java
关注(0)|答案(14)|浏览(455)

我想处理两个数字相乘导致溢出的特殊情况。代码如下所示:

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

uyhoqukh

uyhoqukh1#

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

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"
}
watbbzwu

watbbzwu2#

正如已经指出的,Java8有math.exact方法,这些方法在溢出时抛出异常。
如果您的项目没有使用Java8,您仍然可以“借用”它们的实现,它们非常紧凑。
下面是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
等等,等等。
更新:切换到第三方网站的无效链接到openjdk的mercurial存储库的链接。

iq0todco

iq0todco3#

我想建立在约翰库格曼的答案,而不是取代它直接编辑。它适用于他的测试用例( MIN_VALUE = -10 , MAX_VALUE = 10 )因为 MIN_VALUE == -MAX_VALUE ,这不是2的补码整数的情况。实际上, 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 == 其他的(凯尔首先提出的观点)。下面是一个解决方法:

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 以及 b .

4urapxun

4urapxun4#

Java8有 Math.multiplyExact , Math.addExact 对于int和long等。这些都是不受约束的 ArithmeticException 溢出时。

42fyovps

42fyovps5#

也许这会帮助你:

/**
 * @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;
    }
}
2exbekwf

2exbekwf6#

java库提供了安全的算术运算,用于检查长溢出/下溢。例如,guava的longmath.checkedmultiply(long a,long b)返回 a 以及 b ,前提是它不溢出,并抛出 ArithmeticException 如果 a * b 签名中溢出 long 算术。

fquxozlt

fquxozlt7#

我不知道为什么没有人会考虑这样的解决方案:

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

从两个数字中选择一个较大的。

f4t66c6m

f4t66c6m8#

您可以改用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()
}
j1dl9f46

j1dl9f469#

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

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

编辑:看到有问题的long.max\u值

1mrurvl1

1mrurvl110#

也许 吧:

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

不确定这个“解决方案”。
编辑:添加了b!=0
在你否决之前:a*b/b不会被优化。这可能是编译器错误。我仍然没有看到可以掩盖溢出错误的情况。

ghhaqwfi

ghhaqwfi11#

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

这里有一张小table,我用它来检查,假装溢出发生在-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
bogh5gae

bogh5gae12#

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.以大字体保存结果(intint将结果放入long,longlong放入int64)
2.cmp结果>>位和结果>>(位-1)

bksxznpy

bksxznpy13#

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

5gfr0r5j

5gfr0r5j14#

从jruby偷来的

long result = a * b;
    if (a != 0 && result / a != b) {
       // overflow
    }

更新:这个代码很短,效果很好;但是,对于a=-1,b=long.min\u值,它将失败。
一个可能的增强:

long result = a * b;
if( (Math.signum(a) * Math.signum(b) != Math.signum(result)) || 
    (a != 0L && result / a != b)) {
    // overflow
}

注意,这将捕获一些没有任何除法的溢出。

相关问题