Ключом здесь является просто смещение точки отсчета в правильное место . Возьмите простой пример из формата 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;