Как написано в другом вопросе, математику без знака можно сделать так:
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) по более широкому целому.
Это именно то, что компилятор делает для битовых полей. Кодируете ли вы их вручную или как битовые поля, зависит только от вас, но просто убедитесь, что вы правильно поняли магические числа.
(я не читал стандарт, но подозреваю, что полагаться на битовые поля для правильной работы при переполнении может быть небезопасно?)