c - 将两个 64 位整数乘以 128 位然后 >> 乘以 64 位的最快方法?

标签 c integer fixed-point int128

<分区>

我需要将两个有符号的 64 位整数相乘 ab一起,然后将(128 位)结果转换为带符号的 64 位整数。最快的方法是什么?

我的 64 位整数实际上用 fmt 表示定点数小数位。 fmt选择使得 a * b >> fmt不应溢出,例如 abs(a) < 64<<fmtabs(b) < 2<<fmtfmt==56永远不会在 64 位中溢出,因为最终结果将是 < 128<<fmt因此适合 int64。

我想这样做的原因是为了快速准确地计算 ((((c5*x + c4)*x + c3)*x + c2)*x + c1)*x + c0 形式的五次多项式。在定点格式中,每个数字都有一个带符号的 64 位定点数 fmt小数位。我正在寻找实现这一目标的最有效方法。

最佳答案

正如该问题的评论者所指出的,这最容易通过依赖于机器的代码而不是通过可移植代码高效地完成。提问者指出主要平台是 x86_64,并且有一个内置指令用于执行 64 ✕ 64 → 128 位乘法。使用一小段内联汇编很容易访问。请注意,内联汇编的细节可能因编译器而有所不同,以下代码是使用英特尔 C/C++ 编译器构建的。

#include <stdint.h>

/* compute mul_wide (a, b) >> s, for s in [0,63] */
int64_t mulshift (int64_t a, int64_t b, int s)
{
    int64_t res;
    __asm__ (
        "movq  %1, %%rax;\n\t"          // rax = a
        "movl  %3, %%ecx;\n\t"          // ecx = s
        "imulq %2;\n\t"                 // rdx:rax = a * b
        "shrdq %%cl, %%rdx, %%rax;\n\t" // rax = int64_t (rdx:rax >> s)
        "movq  %%rax, %0;\n\t"          // res = rax
        : "=rm" (res)
        : "rm"(a), "rm"(b), "rm"(s)
        : "%rax", "%rdx", "%ecx");
    return res;
}

与上述代码等效的可移植 C99 如下所示。我已经针对内联汇编版本对此进行了广泛的测试,没有发现不匹配的情况。

void umul64wide (uint64_t a, uint64_t b, uint64_t *hi, uint64_t *lo)
{
    uint64_t a_lo = (uint64_t)(uint32_t)a;
    uint64_t a_hi = a >> 32;
    uint64_t b_lo = (uint64_t)(uint32_t)b;
    uint64_t b_hi = b >> 32;

    uint64_t p0 = a_lo * b_lo;
    uint64_t p1 = a_lo * b_hi;
    uint64_t p2 = a_hi * b_lo;
    uint64_t p3 = a_hi * b_hi;

    uint32_t cy = (uint32_t)(((p0 >> 32) + (uint32_t)p1 + (uint32_t)p2) >> 32);

    *lo = p0 + (p1 << 32) + (p2 << 32);
    *hi = p3 + (p1 >> 32) + (p2 >> 32) + cy;
}

void mul64wide (int64_t a, int64_t b, int64_t *hi, int64_t *lo)
{
    umul64wide ((uint64_t)a, (uint64_t)b, (uint64_t *)hi, (uint64_t *)lo);
    if (a < 0LL) *hi -= b;
    if (b < 0LL) *hi -= a;
}

/* compute mul_wide (a, b) >> s, for s in [0,63] */
int64_t mulshift (int64_t a, int64_t b, int s)
{
    int64_t res;
    int64_t hi, lo;
    mul64wide (a, b, &hi, &lo);
    if (s) {
        res = ((uint64_t)hi << (64 - s)) | ((uint64_t)lo >> s);
    } else {
        res = lo;
    }
    return res;
}

关于c - 将两个 64 位整数乘以 128 位然后 >> 乘以 64 位的最快方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31652875/

相关文章:

c - 它会定义结构的结构吗?

c++ - 如何在 rstudio 上为使用子目录内 src 文件夹中的 C/C++ 文件的包构建和加载共享库?

c - 如何用C语言实现剪贴板

C++ 将整数的大小压缩到 2 位?

c++ - .cpp 文件中的两个 namespace 用于比较

optimization - 这些天什么时候使用定点

c - 内核如何检测内核空间段错误?

C - 从数组中删除重复项并将这些重复项存储在另一个数组中

java - 确定 JTextField 是否包含整数

c++ - 使用定点代替浮点的快速平方根求逆