为什么以这种方式执行乘法?

mpe*_*kov 9 c++ fixed-point multiplication

我遇到过这个函数:

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;
}
Run Code Online (Sandbox Code Playgroud)

内联评论是我的.似乎所有它正在做的是将两个参数相乘.这是正确的,还是还有更多呢?为什么要以这种方式完成?

Der*_*ter 15

这些参数不代表整数.它们以定点格式表示实数,小数点右侧为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
Run Code Online (Sandbox Code Playgroud)

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

 return (o32 * o16 + 0x4000) >> 15;
Run Code Online (Sandbox Code Playgroud)

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


ana*_*lyg 5

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

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

注意,存在一个拐角情况,其中每个数字具有最小值(-2 ^ 15和-2 ^ 31); 在这种情况下,结果(2 ^ 31)在输出中不可表示,并被包裹(变为-2 ^ 31).对于所有其他组合o16o32,结果是正确的.