Как выполнить деление на два целых числа с фиксированной точкой 32Q16? - PullRequest
0 голосов
/ 01 ноября 2019

Я пытаюсь разделить два числа 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);
}

1 Ответ

2 голосов
/ 01 ноября 2019

Функция отображения, определенная 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...