4

有没有办法long在Java中获得两个s相乘的高半部分?即由于溢出而消失的部分。(所以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 中是否可行,如果可以,如何实现?

4

7 回答 7

5

Java 9 有Math.multiplyHigh,根据 Javadocs “返回两个 64 位因子的 128 位乘积的最高有效 64 位。”

于 2018-08-14T07:00:32.227 回答
4

接受的解决方案在大多数情况下都是错误的(66%),尽管错误是有界的(它最多可以比精确结果小 2 并且永远不会更大)。这来自

  • 忽略x_lo * y_lo产品
  • 首先移位,然后添加x_hi * y_lox_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();
于 2015-07-25T17:35:39.887 回答
3

假设您有两个 long ,xand y, and x = x_hi * 2^32 + x_lo, and 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);
于 2013-09-17T20:42:09.253 回答
3

如果 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);
于 2016-08-10T17:38:56.190 回答
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;
}
于 2019-03-30T10:03:23.240 回答
1

这是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 中,可能应该直接调用它。发布来源只是为了展示“幕后”发生的事情。

于 2019-01-16T10:30:00.763 回答
0

您应该考虑使用BigInteger

于 2013-09-17T20:40:07.647 回答