如何检测Java中“无符号”长乘法的溢出?

时间:2013-11-18 22:41:55

标签: java long-integer multiplication integer-overflow unsigned-integer

当然,Java没有“无符号”长值,但有时签名长值有效地被视为无符号长值(例如System.nanoTime()的结果)。从这个意义上说,算术溢出并不意味着值的溢出,而是指64位表示的溢出。例子:

Long.MAX_VALUE * 2L  // overflows the signed product but not the unsigned product
Long.MAX_VALUE * 4L  // overflows the signed product and the unsigned product
-1L * 2L             // overflows the unsigned product but not the signed product

测试乘法溢出是否有点复杂,因为操作的符号会妨碍。值得注意的是,任何负值乘以0或1以外的任何值都会溢出无符号乘积,因为负值的最高位已设置。

确定两个“无符号”长值的乘积(实际上是有符号长值)的最佳方法是什么才会溢出64位表示?使用BigInteger的实例是一个明显的解决方案,我已经得出了一个只涉及原始操作的复杂测试,但我觉得我错过了一些明显的东西。

3 个答案:

答案 0 :(得分:2)

鉴于我们假装为无符号长值的两个有符号长整数值,下面是如何使用仅有符号的原始操作来确定无符号乘积是否会溢出(请原谅这个属性):

boolean unsignedMultiplyOverflows(final long a, final long b) {
    if ((a == 0L) || (b == 0L)) {
        // Unsigned overflow of a * b will not occur, since the result would be 0.
        return false;
    }
    if ((a == 1L) || (b == 1L)) {
        // Unsigned overflow of a * b will not occur, since the result would be a or b.
        return false;
    }
    if ((a < 0L) || (b < 0L)) {
        // Unsigned overflow of a * b will occur, since the highest bit of one argument is set, and a bit higher than the lowest bit of the other argument is set.
        return true;
    }
    /*
     * 1 < a <= Long.MAX_VALUE
     * 1 < b <= Long.MAX_VALUE
     *
     * Let n == Long.SIZE (> 2), the number of bits of the primitive representation.
     * Unsigned overflow of a * b will occur if and only if a * b >= 2^n.
     * Each side of the comparison must be re-written such that signed overflow will not occur:
     *
     *     [a.01]  a * b >= 2^n
     *     [a.02]  a * b > 2^n - 1
     *     [a.03]  a * b > ((2^(n-1) - 1) * 2) + 1
     *
     * Let M == Long.MAX_VALUE == 2^(n-1) - 1, and substitute:
     *
     *     [a.04]  a * b > (M * 2) + 1
     *
     * Assume the following identity for non-negative integer X and positive integer Y:
     *
     *     [b.01]  X == ((X / Y) * Y) + (X % Y)
     *
     * Let X == M and Y == b, and substitute:
     *
     *     [b.02]  M == ((M / b) * b) + (M % b)
     *
     * Substitute for M:
     *
     *     [a.04]  a * b > (M * 2) + 1
     *     [a.05]  a * b > ((((M / b) * b) + (M % b)) * 2) + 1
     *     [a.06]  a * b > ((M / b) * b * 2) + ((M % b) * 2) + 1
     *
     * Assume the following identity for non-negative integer X and positive integer Y:
     *
     *     [c.01]  X == ((X / Y) * Y) + (X % Y)
     *
     * Let X == ((M % b) * 2) + 1 and Y == b, and substitute:
     *
     *     [c.02]  ((M % b) * 2) + 1 == (((((M % b) * 2) + 1) / b) * b) + ((((M % b) * 2) + 1) % b)
     *
     * Substitute for ((M % b) * 2) + 1:
     *
     *     [a.06]  a * b > ((M / b) * b * 2) + ((M % b) * 2) + 1
     *     [a.07]  a * b > ((M / b) * b * 2) + (((((M % b) * 2) + 1) / b) * b) + ((((M % b) * 2) + 1) % b)
     *
     * Divide each side by b (// represents real division):
     *
     *     [a.08]  (a * b) // b > (((M / b) * b * 2) + (((((M % b) * 2) + 1) / b) * b) + ((((M % b) * 2) + 1) % b)) // b
     *     [a.09]  (a * b) // b > (((M / b) * b * 2) // b) + ((((((M % b) * 2) + 1) / b) * b) // b) + (((((M % b) * 2) + 1) % b) // b)
     *
     * Reduce each b-divided term that otherwise has a known factor of b:
     *
     *     [a.10]  a > ((M / b) * 2) + ((((M % b) * 2) + 1) / b) + (((((M % b) * 2) + 1) % b) // b)
     *
     * Let c == ((M % b) * 2) + 1), and substitute:
     *
     *     [a.11]  a > ((M / b) * 2) + (c / b) + ((c % b) // b)
     *
     * Assume the following tautology for integers X, Y and real Z such that 0 <= Z < 1:
     *
     *     [d.01]  X > Y + Z <==> X > Y
     *
     * Assume the following tautology for non-negative integer X and positive integer Y:
     *
     *     [e.01]  0 <= (X % Y) // Y < 1
     *
     * Let X == c and Y == b, and substitute:
     *
     *     [e.02]  0 <= (c % b) // b < 1
     *
     * Let X == a, Y == ((M / b) * 2) + (c / b), and Z == ((c % b) // b), and substitute:
     *
     *     [d.01]  X > Y + Z <==> X > Y
     *     [d.02]  a > ((M / b) * 2) + (c / b) + ((c % b) // b) <==> a > ((M / b) * 2) + (c / b)
     *
     * Drop the last term of the right-hand side:
     *
     *     [a.11]  a > ((M / b) * 2) + (c / b) + ((c % b) // b)
     *     [a.12]  a > ((M / b) * 2) + (c / b)
     *
     * Substitute for c:
     *
     *     [a.13]  a > ((M / b) * 2) + ((((M % b) * 2) + 1) / b)
     *
     * The first term of the right-hand side is clearly non-negative.
     * Determine the upper bound for the first term of the right-hand side (note that the least possible value of b == 2 produces the greatest possible value of (M / b) * 2):
     *
     *     [f.01]  (M / b) * 2 <= (M / 2) * 2
     *
     * Assume the following tautology for odd integer X:
     *
     *     [g.01]  (X / 2) * 2 == X - 1
     *
     * Let X == M and substitute:
     *
     *     [g.02]  (M / 2) * 2 == M - 1
     *
     * Substitute for (M / 2) * 2:
     *
     *     [f.01]  (M / b) * 2 <= (M / 2) * 2
     *     [f.02]  (M / b) * 2 <= M - 1
     *
     * The second term of the right-hand side is clearly non-negative.
     * Determine the upper bound for the second term of the right-hand side (note that the <= relation is preserved across positive integer division):
     *
     *     [h.01]  M % b < b
     *     [h.02]  M % b <= b - 1
     *     [h.03]  (M % b) * 2 <= (b - 1) * 2
     *     [h.04]  ((M % b) * 2) + 1 <= (b * 2) - 1
     *     [h.05]  (((M % b) * 2) + 1) / b <= ((b * 2) - 1) / b
     *     [h.06]  (((M % b) * 2) + 1) / b <= 1
     *
     * Since the upper bound of the first term is M - 1, and the upper bound of the second term is 1, the upper bound of the right-hand side is M.
     * Each side of the comparison has been re-written such that signed overflow will not occur.
     */
    final boolean unsignedMultiplyOverflows = (a > ((Long.MAX_VALUE / b) * 2L) + ((((Long.MAX_VALUE % b) * 2L) + 1L) / b));
    return unsignedMultiplyOverflows;
}

答案 1 :(得分:2)

如果通过确定哪个值更大来开始,则会有更大和更小数字的某些值,以确保可以或不可能发生溢出。假设X更大; Y更小。

如果X低于2 ^ 31,或者如果Y小于2,则无法溢出;否则,如果X大于2 ^ 62或Y不小于2 ^ 32,则溢出是确定的。如果适用任何条件,则返回。

否则,由于X的下限,已知V =(X>> 31)&lt;&lt; 31在X和X / 2之间。由于X的上限,V>&gt; 31小于2 ^ 31 Y小于2 ^ 32,T =(V> 31)* Y(也等于(X> 31)* Y)可以计算没有溢出。因为V是2 ^ 31的倍数,所以T也等于(V * Y)> 31,因此我们知道T在(X * Y)> 31和(X * Y)之间&gt;&gt; ; 32

如果T小于2 ^ 31,那么X * Y必须小于2 ^ 63并且不可能溢出。如果T不小于2 ^ 32,则X * Y必须至少为2 ^ 63且溢出是确定的。

如果两种情况均不适用,则产品将在2 ^ 62到2 ^ 64的范围内。可以通过直接进行乘法并检查结果的符号来确定溢出。与C不同,其中有符号整数溢出产生未定义行为,Java保证如果x和y为正且x * y小于2 ^ 64,则算术溢出将产生否定结果。

总之,代码应该从排名X和Y开始,然后进行四次比较和条件返回。如果它们都没有产生决定性结果,则可以计算(X>> 31)* Y并进行另外两次比较。如果那些没有产生决定性的结果,再多一次和测试将产生最终答案,在最坏的情况下,使用八次比较,一次移位和两次乘法(如果X和Y的等级未知,则添加另一个比较它们)。

请注意,如果原始数字可能为负数,则需要进行更多检查以处理一些额外情况。尽管如此,上述方法应该比需要一个或多个划分的方法更快。

答案 2 :(得分:1)

修改
正如我对原始帖子的评论中所承诺的那样,我现在将发布严格而正确的解决方案。 Nathan自己的公式(对于那些感兴趣的人在他的答案中看到最后一个代码行)的分歧较少,但它有额外的分支,所以我不确定它会在性能方面更好。
而且,唉,它不是单行。这是:

    static boolean unsignedMultiplyOverflows(final long a, final long b) {
        if (a == 0 || b == 0) {
            return false;
        }

        // now proceed with non-zero input
        // we branch based upon parity of a and b
        final long aHalf = a >>> 1;
        final long bHalf = b >>> 1;
        final byte aLastBit = (byte) (a & 1);
        final byte bLastBit = (byte) (b & 1);
        if (aLastBit == 0) { // a = 2 * aHalf, meaning unsigned representation of a
            return Long.MAX_VALUE / b < aHalf;
        } else if (bLastBit == 0) { // b = 2 * bHalf
            return Long.MAX_VALUE / a < bHalf; // symmetrical to previous case
        } else { // a = 2 * aHalf + 1; b = 2 * bHalf + 1
            return (Long.MAX_VALUE - bHalf) / b < aHalf;
        }
    }

正式证据基于2个案例的调查,1。至少有一个乘数是偶数,而2. ab都是奇数。如果有人有兴趣,我可以添加它 我已经在全范围的字符上进行了单元测试:0~0xffff用于16位数字的溢出,以及一些随机long输入,将结果与Nathan的方法和{{1}进行比较}解决方案作为参考
希望有所帮助。