9

我遇到了这个功能:

static inline INT32 MPY48SR(INT16 o16, INT32 o32)
{
    UINT32   Temp0;
    INT32    Temp1;
    // A1. get the lower 16 bits of the 32-bit param
    // A2. multiply them with the 16-bit param
    // A3. add 16384 (TODO: why?)
    // A4. bitshift to the right by 15 (TODO: why 15?)
    Temp0 = (((UINT16)o32 * o16) + 0x4000) >> 15;
    // B1. Get the higher 16 bits of the 32-bit param
    // B2. Multiply them with the 16-bit param
    Temp1 = (INT16)(o32 >> 16) * o16;
    // 1. Shift B to the left (TODO: why do this?)
    // 2. Combine with A and return
    return (Temp1 << 1) + Temp0;
}

内联评论是我的。似乎它所做的只是将这两个参数相乘。这是正确的,还是还有更多?为什么会这样做呢?

4

2 回答 2

16

这些参数不代表整数。它们以定点格式表示实数,小数点右侧有 15 位。例如,1.0 表示为 1 << 15 = 0x8000,0.5 表示 0x4000,-0.5 表示 0xC000(或 32 位中的 0xFFFFC000)。

添加定点数很简单,因为您只需添加它们的整数表示。但是如果要相乘,首先必须将它们作为整数相乘,但是小数点右侧的位数是原来的两倍,因此必须通过移位丢弃多余的位。例如,如果您想以 32 位格式将 0.5 与自身相乘,则将 0x00004000 (1 << 14) 与自身相乘得到 0x10000000 (1 << 28),然后右移 15 位得到 0x00002000 (1 < < 13)。为了获得更好的准确性,当您丢弃最低 15 位时,您希望四舍五入到最接近的数字,而不是向下舍入。您可以通过添加 0x4000 = 1 << 14 来做到这一点。然后如果丢弃的 15 位小于 0x4000,则向下舍入,如果为 0x4000 或更大,则向上舍入。

 (0x3FFF + 0x4000) >> 15 = 0x7FFF >> 15 = 0
 (0x4000 + 0x4000) >> 15 = 0x8000 >> 15 = 1

总而言之,您可以像这样进行乘法运算:

 return (o32 * o16 + 0x4000) >> 15;

但是有一个问题。在 C++ 中,乘法的结果与其操作数具有相同的类型。Soo16提升为与 相同的大小o32,然后将它们相乘得到 32 位结果。但这会丢弃最高位,因为该产品需要 16 + 32 = 48 位才能准确表示。一种方法是将操作数转换为 64 位,然后相乘,但这可能会更慢,并且并非所有机器都支持。因此,它改为o32分成两个 16 位片段,然后在 32 位中进行两次乘法运算,然后合并结果。

于 2012-10-12T18:43:16.817 回答
5

这实现了定点数的乘法。这些数字被视为采用 Q15 格式(小数部分有 15 位)。

在数学上,此函数计算(o16 * o32) / 2^15,四舍五入到最接近的整数(因此2^14,表示 的因子被1/2添加到一个数字中以对其进行四舍五入)。它使用无符号和有符号的 16 位乘法与 32 位结果,这可能是指令集支持的。

请注意,存在一个极端情况,其中每个数字都有一个最小值(-2^15 和 -2^31);在这种情况下,结果 (2^31) 无法在输出中表示,并被包裹起来(变为 -2^31)。对于 和 的所有其他组合o16o32结果是正确的。

于 2012-10-12T18:37:52.080 回答