簡體   English   中英

Java中的高位長乘法?

[英]high bits of long multiplication in Java?

有沒有辦法在Java中獲得兩個long s的乘法的高半? 即由於溢出而消失的部分。 (所以 128 位結果的高 64 位)

我習慣於編寫 OpenCL 代碼,其中命令mul_hi正是這樣做的: http : mul_hi

由於 OpenCL 可以在我的 CPU 上有效地完成它,Java 也應該能夠這樣做,但是我找不到我應該如何在 Java 中做到這一點(甚至有效地模仿它的行為)。 這在 Java 中是否可行,如果可以,如何實現?

大多數情況下(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();

Java 9 具有Math.multiplyHigh ,根據 Javadocs 的說法“將兩個 64 位因子的 128 位乘積的最高有效 64 位作為長返回。”

假設您有兩個 long, xy ,以及x = x_hi * 2^32 + x_loy = 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);

如果 x 或 y 可以為負數,則應使用 Hacker's Delight 函數(Henry S. Warren, Hacker's Delight, Addison-Wesley, 2nd edition, Fig. 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);

這是 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 中,可能應該直接調用它。 發布源代碼只是為了展示“幕后”發生的事情。

上面描述的一些情況是錯誤的。 首先,您必須問自己您處理什么類型的操作數(有符號/無符號)。

上面示例中有一個修改后的代碼,該代碼針對進位標志是固定的(將 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;
}

您應該考慮使用BigInteger

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM