3

我正在 16 位处理器上编写定点语音增强算法。在某些时候,我需要进行 32 位小数乘法。我已经阅读了其他有关逐字节进行 32 位乘法的帖子,我明白了为什么这适用于 Q0.31 格式。但我使用不同数量的小数位的不同 Q 格式。

所以我发现对于小于 16 的小数位,这是有效的:

(low*low >> N) + low*high + high*low + (high*high << N)

其中 N 是小数位数。我已经读过low*low结果应该是无符号的以及低字节本身。一般来说,这完全给出了我想要的任何 Q 格式的结果,并且小数位小于 16。

现在,当小数位超过 16 位时,它会变得很棘手。我尝试了几种移位,不同的移位,low*lowhigh*high试图把它写在纸上,但我无法弄清楚。

我知道这可能非常简单,但整个想法让我难以理解,我将不胜感激一些评论或指导!

4

2 回答 2

0

这是同一个公式。对于 N > 16,移位仅意味着您丢弃了一个完整的 16 位字,该字会溢出或下溢。low*low >> N 表示只在乘法的 32 位结果的高字中移动 N-16 位并添加到结果的低字中。high * high << N 表示只使用左移 N-16 的乘法结果的低字并添加到结果的高字。

于 2013-02-05T18:17:11.597 回答
0

有一些想法在起作用。

首先,将 2 个较短的整数相乘以产生较长的乘积。考虑 2 个 32 位整数的无符号乘法,通过它们的 16 位“一半”相乘,每个都产生一个 32 位乘积,总乘积为 64 位:

  a * b = (a_hi * 2 16 + a_lo) * (b_hi * 2 16 + b_lo) =

  a_hi * b_hi * 2 32 + (a_hi * b_lo + a_lo * b_hi) * 2 16 + a_lo * b_lo。

现在,如果你需要一个有符号乘法,你可以从无符号乘法中构造它(例如从上面)。

假设 a < 0 且 b >= 0,a * 有符号b 必须相等

  2 64 - ((-a) *无符号b),其中

  -a = 2 32 - a(因为这是 2 的补码)

爱荷华州,

  a *签名b =

  2 64 - ((2 32 - a) *无符号b) =

  2 64 + (a * unsigned b) - (b * 2 32 ),其中 2 64可以被丢弃,因为我们只使用 64 位。

以完全相同的方式,您可以计算 a >= 0 和 b < 0 的带符号b 并且必须得到对称结果:

  (a *无符号b) - (a * 2 32 )

您可以类似地表明,对于 a < 0 和 b < 0,可以通过以下方式在无符号乘法之上构建有符号乘法:

  (a *无符号b) - ((a + b) * 2 32 )

因此,首先将 a 和 b 相乘为无符号,然后如果 a < 0,则从乘积的前 32 位中减去 b,如果 b < 0,则从乘积的前 32 位中减去 a,完成。


现在我们可以将 32 位有符号整数相乘并得到 64 位有符号乘积,我们终于可以转向小数部分了。

现在假设在 a 和 b 的这 32 位中,N 位用于小数部分。这意味着如果您将 a 和 b 视为普通整数,它们将比它们实际表示的值大 2 N倍,例如 1.0 看起来像 2 N(或 1 << N)。

因此,如果您将两个这样的整数相乘,则乘积将是 2 N *2 N = 2 2*N倍于它应该表示的值,例如 1.0 * 1.0 看起来像 2 2*N(或 1 << (2*N))。IOW,普通整数乘法将使小数位数增加一倍。如果您希望产品具有与被乘数相同的小数位数,您会怎么做?您将乘积除以 2 N(或将其以算术方式向右移动 N 个位置)。简单的。


谨记几句,以防万一……

在 C(和 C++)中,您不能合法地将变量向左或向右移动变量中包含的相同或更多位数。代码会编译,但不会像您期望的那样工作。因此,如果要移动 32 位变量,可以将其向左或向右移动 0 到 31 个位置(31 是最大值,而不是 32)。

如果将有符号整数左移,则不能合法地溢出结果。所有带符号的溢出都会导致未定义的行为。所以,你可能想坚持未签名。

负符号整数的右移是特定于实现的。它们可以进行算术移位或逻辑移位。哪一个,这取决于编译器。因此,如果您需要两者之一,您需要确保您的编译器直接支持它或以其他方式实现它。

于 2013-02-05T18:54:54.940 回答