当然,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
的实例是一个明显的解决方案,我已经得出了一个只涉及原始操作的复杂测试,但我觉得我错过了一些明显的东西。
答案 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. a
,b
都是奇数。如果有人有兴趣,我可以添加它
我已经在全范围的字符上进行了单元测试:0~0xffff用于16位数字的溢出,以及一些随机long
输入,将结果与Nathan的方法和{{1}进行比较}解决方案作为参考
希望有所帮助。