Преобразование между 64-разрядными и 32-разрядными числами с фиксированной запятой - PullRequest
0 голосов
/ 11 марта 2020

Как преобразовать данные из формата Q33.31 в формат Q2.30? Я знаю, что нам нужно использовать операторы сдвига, если и вход, и выход имеют одинаковый битовый размер. Но как рассчитать, если они разного размера?

Ответы [ 3 ]

3 голосов
/ 11 марта 2020

Ключом здесь является просто смещение точки отсчета в правильное место . Возьмите простой пример из формата Q9.7 в Q2.6, например:

in  9 8 7 6 5 4 3 2 1.1 2 3 4 5 6 7
out                 2 1.1 2 3 4 5 6

. Как вы можете видеть, позиции радикс точки выхода равны 1 справа от входа, поэтому мы нужно сдвинуть вправо, чтобы поставить его в нужное положение. Вы также можете думать так: в дробной части вывода на 1 бит меньше, поэтому мы сместим вправо на 1 бит, чтобы сократить его с 7 до 6 бит. 7 старших бит целой части будут автоматически обрезаны в C, когда вы сделаете присваивание более узкому типу. Это эквивалентно

uint8_t out = in >> 1;

Аналогично для преобразования из Q33.31 в Q2.30 вы сделаете то же самое: q2_30 = q33_31 >> 1

Однако теперь, чтобы получить более правильный результат, вы нужно сделать шаг округления. Существует много методов округления, но самый простой способ - это просто округление до ближайшего , проверяя, является ли значение выше или ниже 0,5. Как в десятичном, где мы проверяем первый усеченный di git, чтобы увидеть, если он> = 5 или нет, в двоичном коде мы проверяем последний бит, который был сдвинут, и добавляем его обратно к результату, как это

uint32_t q2_30 = (q33_31 >> 1) + (q33_31 & 1)

Редактировать

Совершенно не нужно выполнять усечение, чтобы сделать это, когда вы просто хотите получить сумму двух битовых чисел Q1.31. Просто конвертируйте их в Q2.30, используя описанный выше метод, затем добавьте округлить позже

uint32_t A2_30 = A1_31 >> 1; // types must be unsigned so that the shifts are logical
uint32_t B2_30 = B1_31 >> 1; // instead of arithmetic

// if only one of the values is 1 then their sum is 0.5 ULP which will be rounded to 1
uint32_t carry = (A1_31 & 1) | (B1_31 & 1); // if both of them are 1 then sum = 1 ULP

Q2_30 sum = A2_30 + B2_30 + carry;
2 голосов
/ 11 марта 2020

В комментарии к ответу @ goodvibration вы утверждаете, что добавляете две цифры Q1.31. Учитывая это, вы знаете, что ваш результат представлен в виде Q2.31, поэтому для преобразования вашего номера Q2.31 в Q2.30 вам просто нужно сдвинуть результат вправо на один бит:

uint32_t convert_q231_q230(uint64_t x)
  {
  return (uint32_t) (x >> 1);
  }
0 голосов
/ 11 марта 2020

Как насчет этого:

uint32_t convert(uint64_t x)
{
    uint32_t hi = (uint32_t)(x >> 32);
    uint32_t lo = (uint32_t)(x);
    if (hi >= (1 << 2) || lo >= (1 << 30))
        // handle input-too-large-or-too-accurate error and exit
    return (hi << 30) | lo;
}

В качестве альтернативы обработке ошибочного ввода в операторе if (если вас не волнует возможная потеря информации), вы можете просто return (hi << 30) | ((lo << 2) >> 2);.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...