Как объединить две битовые переменные со знаком в одну битовую переменную со знаком?

Предположим, следующий код C ++:

#include <iostream>
using namespace std;

typedef struct
{
int a: 5;
int b: 4;
int c: 1;
int d: 22;

} example;

int main()
{
example blah;

blah.a = -5; // 11011
blah.b = -3; // 1101

int result = blah.a << 4 | blah.b;

cout << "Result = " << result << endl; // equals 445 , but I am interested in this having a value of -67

return 0;
}

Я заинтересован в том, чтобы переменная result имела тип int, где 9-й бит является наиболее значимым битом. Я хотел бы, чтобы это было так, чтобы результат = -67 вместо 445. Как это сделать? Благодарю.

0

Решение

Увидеть Знак Расширение int в С за тесно связанный вопрос (но не дубликат).

Вы должны знать, что почти все в битовых полях «определяется реализацией». В частности, не ясно, что вы можете присвоить отрицательные числа intбитовое поле; Вы должны знать, использует ли ваша реализация int является signedили int является unsigned». Который является 9го немного становится сложнее; считаете ли вы от 0 или 1, и какой конец набора битовых полей находится на бите 0, а какой на бите 31 (считая младший значащий бит (LSB) как бит 0 и старший значащий бит (MSB) как бит 31 32-битное количество). Действительно, размер вашей структуры не должен быть 32-битным; Компилятор может иметь разные правила для макета.

Со всеми этими предостережениями у вас есть 9-битное значение, сформированное из (blah.a << 4) | blah.bи вы хотите, чтобы этот знак был расширен, как если бы это был 9-битный номер дополнения до 2 (32-битный) int,

Функция в ответе с перекрестными ссылками может сделать работу:

#include <assert.h>
#include <limits.h>

extern int getFieldSignExtended(int value, int hi, int lo);

enum { INT_BITS = CHAR_BIT * sizeof(int) };
int getFieldSignExtended(int value, int hi, int lo)
{
assert(lo >= 0);
assert(hi > lo);
assert(hi < INT_BITS - 1);
int bits = (value >> lo) & ((1 << (hi - lo + 1)) - 1);
if (bits & (1 << (hi - lo)))
return(bits | (~0 << (hi - lo)));
else
return(bits);
}

Вызовите это как:

int result = getFieldSignExtended((blah.a << 4) | blah.b), 8, 0);

Если вы хотите жестко связать числа, вы можете написать:

int x = (blah.a << 4) | blah.b;

int result = (x & (1 << 8)) ? (x | (~0 << 8)) : x;

Обратите внимание, я предполагаю, что 9го бит — это бит 8 значения с битами 0..8. Отрегулируйте, если у вас есть другая интерпретация.


Рабочий код

Составлено с g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-44) от машины RHEL 5 x86 / 64.

#include <iostream>
using namespace std;

typedef struct
{
int a: 5;
int b: 4;
int c: 1;
int d: 22;
} example;

int main()
{
example blah;

blah.a = -5; // 11011
blah.b = -3; // 1101

int result = blah.a << 4 | blah.b;

cout << "Result = " << result << endl;

int x = (blah.a << 4) | blah.b;
cout << "x = " << x << endl;

int result2 = (x & (1 << 8)) ? (x | (~0 << 8)) : x;
cout << "Result2 = " << result2 << endl;

return 0;
}

Образец вывода:

Result = 445
x = 445
Result2 = -67

ISO / IEC 14882: 2011 — Стандарт C ++

§7.1.6.2 Спецификаторы простого типа

¶3 … [Примечание: это определяется реализацией, являются ли объекты char Тип и некоторые битовые поля (9.6)
представлены как подписанные или неподписанные количества. signed спецификатор заставляет объекты char и битовые поля быть
подписан; это избыточно в других контекстах. —Конечная записка]

§9.6 Битовые поля [class.bit]

№1 Член-декларатор формы

 identifier<sub>opt</sub> attribute-specifier-seq<sub>opt</sub>: constant-expression

указывает битовое поле; его длина выделяется из имени битового поля двоеточием. Необязательный атрибут-спецификатор-seq
относится к заявленной сущности. Атрибут битового поля не является частью типа класса
член. Выражение константы должно быть интегральным выражением константы со значением, большим или равным
в ноль. Значение выражения интегральной константы может быть больше, чем число битов в объекте
представление (3.9) типа битового поля; в таких случаях дополнительные биты используются как биты заполнения и не
участвовать в представлении значения (3.9) битового поля. Выделение битовых полей внутри объекта класса
реализации. Выравнивание битовых полей определяется реализацией. Битовые поля упакованы в несколько
адресная единица выделения. [Примечание: битовые поля расположены на одних машинах, а не на других.
Битовые поля назначаются справа налево на некоторых машинах, слева направо на других. —Конечная записка]

Declaration2 Объявление для битового поля, в котором отсутствует идентификатор, объявляет безымянное битовое поле. Безымянные битовые поля
не являются членами и не могут быть инициализированы. [Примечание: безымянное битовое поле полезно для заполнения, чтобы соответствовать
навязанные извне макеты. — примечание конца] Как особый случай, неназванное битовое поле с нулевой шириной
определяет выравнивание следующего битового поля на границе единицы выделения. Только при объявлении неназванного
битовое поле может значение константы-выражения быть равным нулю.

Bit3 Битовое поле не должно быть статическим элементом. Битовое поле должно иметь целочисленный тип или тип перечисления (3.9.1). это
определяется реализацией, является ли обычный (ни явно подписанный, ни неподписанный) тип char, short, int, long,
или длинное длинное битовое поле подписано или не подписано. Значение bool может быть успешно сохранено в битовом поле любого
ненулевой размер. Адрес оператора & не должен применяться к битовому полю, поэтому нет указателей на битовые поля.
Неконстантная ссылка не должна быть привязана к битовому полю (8.5.3). [Примечание: если инициализатор для справки
типа const T& является lvalue, который ссылается на битовое поле, ссылка привязана к временному
сохранить значение битового поля; ссылка не привязана к битовому полю напрямую. Смотрите 8.5.3. —Конечная записка]

If4 Если значение true или false хранится в битовом поле типа bool любого размера (включая однобитовое битовое поле),
исходное значение bool и значение битового поля должны сравниваться одинаково. Если значение перечислителя
хранится в битовом поле с тем же типом перечисления, а количество битов в битовом поле достаточно велико
для хранения всех значений этого типа перечисления (7.2), исходного значения перечислителя и значения
битовое поле должно сравниваться равным. [ Пример:

enum BOOL { FALSE=0, TRUE=1 };
struct A {
BOOL b:1;
};
A a;
void f() {
a.b = TRUE;
if (a.b == TRUE) // yields true
{ /* ... */ }
}

— конец примера]


Стандарт ISO / IEC 9899: 2011 — C2011

Стандарт С имеет по сути тот же эффект, но информация представлена ​​несколько иначе.

6.7.2.1 Структура и объединение спецификаторов

The4 Выражение, которое определяет ширину битового поля, должно быть целочисленной константой.
выражение с неотрицательным значением, которое не превышает ширину объекта
тип, который был бы указан, были бы двоеточие и выражение опущены.122) Если значение
ноль, декларация не должна иметь декларатора.

Bit5 Битовое поле должно иметь тип, который является квалифицированной или неквалифицированной версией _Bool, signed
int
, unsigned intили некоторый другой тип, определенный реализацией. это
определяется реализацией, разрешены ли атомарные типы.

¶9 … Кроме того, член может быть объявлен состоящим из
указанное количество битов (включая знаковый бит, если есть). Такой член называется
битовое поле;124) его ширине предшествует двоеточие.

Bit10 Битовое поле интерпретируется как имеющее целочисленный тип со знаком или без знака, состоящий из
указанное количество бит.125) Если значение 0 или 1 сохраняется в битовом поле ненулевой ширины
тип _Bool, значение битового поля должно сравниваться равным сохраненному значению; _Bool
битовое поле имеет семантику _Bool.

[11] Реализация может выделить любую адресуемую единицу хранения, достаточно большую для хранения битового поля.
Если остается достаточно места, битовое поле, которое следует сразу за другим битовым полем в
структура должна быть упакована в смежные биты одного и того же блока. Если остается недостаточно места,
помещено ли битовое поле, которое не подходит, в следующий блок или перекрывает смежные блоки
реализации. Порядок распределения битовых полей внутри блока (старший порядок для
младший или младший к старшему) определяется реализацией. Выравнивание
адресная единица хранения не указана.

Declaration12 Объявление битового поля без объявления, а только с двоеточием и шириной указывает
безымянное битовое поле.126) Как особый случай, элемент структуры битового поля с шириной 0
указывает, что никакое дополнительное битовое поле не должно быть упаковано в блок, в котором предыдущее битовое поле,
если таковые были размещены.

122) В то время как количество бит в _Bool объект как минимум CHAR_BIT, ширина (номер знака и
значение битов) _Bool может быть только 1 бит.

124) Одинарный & оператор (address-of) не может быть применен к объекту битового поля; таким образом, нет указателей на
или массивы объектов битового поля.

125) Как указано в 6.7.2 выше, если действительный спецификатор типа — int или typedef-name, определенное как int,
затем определяется реализацией, является ли битовое поле подписанным или неподписанным.

126) Безымянный член структуры битового поля полезен для заполнения, чтобы соответствовать внешне наложенному
макеты.

В Приложении J к стандарту определены проблемы переносимости, а в п. J.3 определено поведение, определяемое реализацией. Отчасти это говорит:

J.3.9 Структуры, объединения, перечисления и битовые поля

¶1 — обрабатывается ли битовое поле «обычное» int как битовое поле со знаком или как
битовое поле без знака (6.7.2, 6.7.2.1).

— Допустимые типы битовых полей, отличные от _Bool, sign int и unsigned int
(6.7.2.1).

— разрешены ли атомарные типы для битовых полей (6.7.2.1).

— Может ли битовое поле охватывать границу блока памяти (6.7.2.1).

— Порядок распределения битовых полей в блоке (6.7.2.1).

1

Другие решения

Других решений пока нет …