Как компилятор реализует арифметику битовых полей? - PullRequest
2 голосов
/ 29 ноября 2011

Задавая вопрос о том, как сделать завернутое N-битное вычитание со знаком , я получил следующий ответ:

template<int bits>
int
sub_wrap( int v, int s )
{
    struct Bits { signed int r: bits; } tmp;
    tmp.r = v - s;
    return tmp.r;
}

Это здорово, но как компилятор это реализует? Из этого вопроса я понял, что доступ к битовым полям более или менее аналогичен тому, как это делается вручную, но что делать в сочетании с арифметикой, как в этом примере? Будет ли это так же быстро, как хороший ручной подход?

Ответ на "gcc" в роли "компилятора" был бы хорош, если бы кто-то захотел получить конкретную информацию. Я попытался прочитать сгенерированную сборку, но в данный момент она мне недоступна.

Ответы [ 2 ]

2 голосов
/ 30 ноября 2011

Как написано в другом вопросе, математику без знака можно сделать так:

int tmp = (a - b) & 0xFFF;  /* 12 bit mask.  */

Запись в (12-битное) битовое поле будет делать именно это, со знаком или без знака. Разница лишь в том, что вы можете получить предупреждающее сообщение от компилятора.

Для чтения вам нужно сделать что-то немного другое.

Для математики без знака достаточно сделать следующее:

int result = tmp;  /* whatever bit count, we know tmp contains nothing else.  */

или

int result = tmp & 0xFFF;  /* 12bit, again, if we have other junk in tmp.  */

Для подписанной математики дополнительная магия - это расширение знака:

int result = (tmp << (32-12)) >> (32-12); /* asssuming 32bit int, and 12bit value. */

Все, что нужно, - это реплицировать верхний бит битового поля (бит 11) по более широкому целому.

Это именно то, что компилятор делает для битовых полей. Кодируете ли вы их вручную или как битовые поля, зависит только от вас, но просто убедитесь, что вы правильно поняли магические числа.

(я не читал стандарт, но подозреваю, что полагаться на битовые поля для правильной работы при переполнении может быть небезопасно?)

2 голосов
/ 29 ноября 2011

Компилятор знает о размере и точной позиции r в вашем примере.Предположим, что это похоже на

[xxxxrrrr]

Тогда

tmp.r = X;

можно, например, расширить до (суффикс b, указывающий двоичные литералы, & - битовый, а | - битовый или)

tmp = (tmp & 11110000b)   // <-- get the remainder which is not tmp.r
    | (X   & 00001111b);  // <-- put X into tmp.r and filter away unwanted bits

Представьте, что ваш макет

[xxrrrrxx]   // 4 bits, 2 left-shifts

расширение может быть

tmp = (tmp    & 11000011b)   // <-- get the remainder which is not tmp.r
    | ((X<<2) & 00111100b);  // <-- filter 4 relevant bits, then shift left 2

Как на самом деле выглядит X, будь то сложная формулировка или простолитерал, на самом деле не имеет значения.

Если ваша архитектура не поддерживает такие побитовые операции, все еще существуют умножения и деления на степени два для имитации сдвига, и, вероятно, их также можно использовать для фильтрации нежелательных битов.

...