有没有办法让Java中两个long
的乘法高一半?即由于溢出而消失的部分。 (因此128位结果的高64位)
我习惯编写OpenCL代码,其中命令mul_hi
正是这样做的:http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/mul_hi.html
由于OpenCL可以在我的CPU上有效地完成,因此Java也应该能够这样做,但我无法在Java中找到我应该如何做到这一点(甚至有效地模仿它的行为)。这有可能在Java中,如果是这样,怎么样?
答案 0 :(得分:4)
大多数时候(66%)接受的解决方案是错误的,尽管误差是有限的(它可能小于精确结果最多2,它永远不会更大)。这来自
x_lo * y_lo
产品x_hi * y_lo
和x_lo * y_hi
我的解决方案似乎总是适用于非负操作数。
final long x_hi = x >>> 32;
final long y_hi = y >>> 32;
final long x_lo = x & 0xFFFFFFFFL;
final long y_lo = y & 0xFFFFFFFFL;
long result = x_lo * y_lo;
result >>>= 32;
result += x_hi * y_lo + x_lo * y_hi;
result >>>= 32;
result += x_hi * y_hi;
测试了十亿个随机操作数。对角落案例和一些分析应该进行特殊测试。
处理负操作数会更复杂,因为它禁止使用无符号移位并迫使我们处理中间结果溢出。
如果速度无关紧要(而且很少),我会选择
BigInteger.valueOf(x).multiply(BigInteger.valueOf(y))
.shiftRight(64).longValue();
答案 1 :(得分:3)
假设您有两个长片,x
和y
,x = x_hi * 2^32 + x_lo
和y = y_hi * 2^32 + y_lo
。
然后x * y == (x_hi * y_hi) * 2^64 + (x_hi * y_lo + x_lo * y_hi) * 2^32 + (x_lo * y_lo)
。
因此,该产品的高64位可以按如下方式计算:
long x_hi = x >>> 32;
long y_hi = y >>> 32;
long x_lo = x & 0xFFFFFFFFL;
long y_lo = y & 0xFFFFFFFFL;
long prod_hi = (x_hi * y_hi) + ((x_ hi * y_lo) >>> 32) + ((x_lo * y_hi) >>> 32);
答案 2 :(得分:2)
如果x或y都是负数,你应该使用Hacker's Delight功能(Henry S. Warren,Hacker's Delight,Addison-Wesley,第2版,图8.2):
long x_high = x >>> 32;
long x_low = x & 0xFFFFFFFFL;
long y_high = y >>> 32;
long y_low = y & 0xFFFFFFFFL;
long z2 = x_low * y_low;
long t = x_high * y_low + (z2 >>> 32);
long z1 = t & 0xFFFFFFFFL;
long z0 = t >>> 32;
z1 += x_low * y_high;
return x_high * y_high + z0 + (z1 >>> 32);
答案 3 :(得分:2)
Java 9具有Math.multiplyHigh,根据Javadocs,它“将两个64位因子的128位乘积中的最高有效64位返回为长整数。”
答案 4 :(得分:1)
上述某些情况下工作不正确。首先,您必须问自己要处理哪种类型的操作数(有符号/无符号)。
上面的示例中有一个修改后的代码,该代码已针对进位标志进行固定(将x和y处理为无符号64位值):
public static long productHi(long x, long y) {
final long x_hi = x >>> 32;
final long y_hi = y >>> 32;
final long x_lo = x & 0xFFFFFFFFL;
final long y_lo = y & 0xFFFFFFFFL;
long result = (x_lo * y_lo) >>> 32;
long a = x_hi * y_lo;
long b = x_lo * y_hi;
long sum = a + b + result;
long carry = ((a & b) | (~sum & (a ^ b))) >>> 63;
result = (sum >>> 32) + x_hi * y_hi + (carry << 32);
return result;
}
答案 5 :(得分:0)
您应该使用BigInteger。
答案 6 :(得分:0)
这里是Java Math.multiplyHigh(long,long)
public static long multiplyHigh(long x, long y) {
if (x < 0 || y < 0) {
// Use technique from section 8-2 of Henry S. Warren, Jr.,
// Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 173-174.
long x1 = x >> 32;
long x2 = x & 0xFFFFFFFFL;
long y1 = y >> 32;
long y2 = y & 0xFFFFFFFFL;
long z2 = x2 * y2;
long t = x1 * y2 + (z2 >>> 32);
long z1 = t & 0xFFFFFFFFL;
long z0 = t >> 32;
z1 += x2 * y1;
return x1 * y1 + z0 + (z1 >> 32);
} else {
// Use Karatsuba technique with two base 2^32 digits.
long x1 = x >>> 32;
long y1 = y >>> 32;
long x2 = x & 0xFFFFFFFFL;
long y2 = y & 0xFFFFFFFFL;
long A = x1 * y1;
long B = x2 * y2;
long C = (x1 + x2) * (y1 + y2);
long K = C - A - B;
return (((B >>> 32) + K) >>> 32) + A;
}
}
从Java 9开始,它包含在java.lang.Math中,可能应该直接对其进行调用。发布源代码只是为了展示“幕后”发生的事情。