如何对两个 32Q16 定点整数进行除法?

How to perform division on two 32Q16 fixed-point integers?

我正在尝试使用定点处理算法来除两个 32Q16 数。我的理解是,当我们将一个 32Q16 定点操作数除以另一个时,我们要求结果为 32Q16 数。因此,我们需要一个 64Q32 红利,它是通过对原始 32Q16 红利进行符号扩展,然后左移 16 位来创建的。然后用 64 位被除数和 32 位除数完成除法,得到 32 位商。

我已经编写了这个 C 例程来计算商。除法对正数很好,但是当我尝试除以任何负数时,结果不正确。

我不确定 C 如何处理 64 位整数和 32 位整数之间的除法。谁能帮我理解我做错了什么?谢谢。

int32_t divide32Q16 (int32_t dividend, int32_t divisor)

{
  int32_t quotient = ((int64_t) dividend << 16) / divisor;

  return quotient;
}

当我尝试将 -32761 除以 10 时,预期结果应该是 -3276.1 但我从代码中得到的实际结果是 -3277.9

这是我用来显示数字的代码片段:

void display32Q16 (int32_t num)
{
  int16_t integer = num >> 16;

  uint64_t fraction = ((0x0000FFFF & num) * 10000) >> 16;

  printf("Number = %d.%" PRIu64 "\n", integer, fraction);
}

目前OP定义的显示函数如下:

void display32Q16 (int32_t num)
{
    int16_t integer = num >> 16;
    uint64_t fraction = ((0x0000FFFF & num) * 10000) >> 16;
    printf("Number = %d.%" PRIu64 "\n", integer, fraction);
}

但是,这并不能正确显示负分数,因为与相同大小的正数相比,负数的 2 的补码表示法的大部分位都反转了。它还只为小数部分打印 4 个十进制数字,这并不像它应该的那样精确。

例如,-3276.1 表示为 int32_t 值 -214702489(-3276.1 * 65536,小数部分被丢弃)。它具有与 0xF333E667 相同的位模式。显示函数通过将值左移 16 位(由位模式 0xFFFFF333 表示)并截断为 16 位(由位模式 0xF333 表示)来确定要打印的整数部分。作为有符号整数,该位模式对应于 -3277。要打印的小数部分是通过取低16位来确定的,用位模式0xE667表示,对应十进制数58983,乘以10000,得到589830000,右移16位(除以65536),得到9000。因此,该值打印为 -3277.9000。

这是一个使用 5 个小数位正确显示数字的函数:

void display32Q16 (int32_t num)
{
    int32_t integer = num >> 16;
    uint64_t fraction = num & 0xFFFF;
    const char *xtrasign = "";

    if (integer < 0 && fraction != 0)
    {
        integer++;
        fraction = 65536 - fraction;
    }
    fraction = (fraction * 100000) >> 16;
    if (num < 0 && integer == 0)
    {
        /* special case for number between -1 and 0 */
        xtrasign = "-";
    }
    printf("Number = %s%" PRIi32 ".%05" PRIu64 "\n", xtrasign, integer, fraction);
}

请注意,-32761 / 10 得到的原始数字将显示为 -3276.09999,因为小数部分 0.1 无法用二进制表示。

如下所示四舍五入到小数点后 4 位可能更可取,这也消除了对 64 位数字的需要。

void display32Q16 (int32_t num)
{
    int32_t integer = num >> 16;
    uint32_t fraction = num & 0xFFFF;
    const char *xtrasign = "";

    if (integer < 0 && fraction != 0)
    {
        integer++;
        fraction = 65536 - fraction;
    }
    fraction = ((fraction * 10000) + (1 << 15)) >> 16;
    if (fraction >= 10000)
    {
        /* deal with fraction rounding overflow */
        if (num < 0)
            integer--;
        else
            integer++;
        fraction -= 10000;
    }
    if (num < 0 && integer == 0)
    {
        /* special case for number between -1 and 0 */
        xtrasign = "-";
    }
    printf("Number = %s%" PRIi32 ".%04" PRIu32 "\n", xtrasign, integer, fraction);
}