【问题标题】:fixed point multiplication without 64 bit temporary没有 64 位临时的定点乘法
【发布时间】:2013-02-27 22:22:35
【问题描述】:

您好,我正在为嵌入式系统实现一些定点数学运算,我正在尝试将两个 16.16 定点数相乘而不创建 64 位临时数。到目前为止,这是我想出的生成指令最少的代码。

int multiply(int x, int y){
    int result;
    long long temp = x;
    temp *= y;
    temp >>= 16;
    result = temp;
    return result;
}

此代码的问题在于它使用了一个临时的 64 位整数,这似乎会生成错误的汇编代码。我正在尝试制作一个使用两个 32 位整数而不是 64 位整数的系统。有人知道怎么做吗?

【问题讨论】:

  • 您是否在输出的反汇编转储中看到了 64 位临时文件,或者只是在您的脑海中?仅仅因为源代码有一个long long 类型的变量并不意味着实际上有任何“64 位临时”。在任何体面的 32 位架构上,32x32 乘法会自动生成 64 位结果,为了您(或编译器)的方便,通常分为两个 32 位寄存器。处理这些比将其分解为 4 个 16x16 乘法以避免“64 位临时”更有效。
  • @R.. 是的,我查看了汇编程序转储。它在 EAX 和 EDX 寄存器中使用了两半,但它的实现不是最优的
  • 这肯定比执行四个 MUL次优...
  • @R.. 我的想法是,现在我知道如何在 C 中做到这一点,我可以手动优化汇编来实现它
  • 如果您指定足够高的优化级别,大多数现代 x86 编译器将自动使用结果的高 32 位

标签: c optimization fixed-point


【解决方案1】:

把你的数字想象成每个数字都由两个大“数字”组成。

  A.B
x C.D

数字的“基数”是 2^bit_width,即 2^16 或 65536。

所以,产品是

D*B       + D*A*65536 + C*B*65536 + C*A*65536*65536

但是,要将乘积右移 16,您需要将所有这些项除以 65536,所以

D*B/65536 + D*A       + C*B        + C*A*65536

在 C 中:

uint16_t a = x >> 16;
uint16_t b = x & 0xffff;
uint16_t c = y >> 16;
uint16_t d = y & 0xffff;

return ((d * b) >> 16) + (d * a) + (c * b) + ((c * a) << 16);

签名版本有点复杂;通常最容易对xy 的绝对值进行算术运算,然后修复符号(除非溢出,您可以相当繁琐地检查)。

【讨论】:

  • 完美的正是我想要的。
  • 这会比你最初尝试的方式慢很多。
  • 嗯,速度取决于嵌入式处理器的能力(请注意,我在提到 EAX 寄存器之前回答了这个问题!)。在 16 位处理器上,我的方法会稍微快一些,因为 32x32->64 位乘法也将有 4 个 16 位乘法,但还必须进行 64 位加法,而我的方法只需要 32 位加法(并且没有实际变化,因为在 16 位处理器上,产品的 16 位字将是单独可访问的)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多