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

Предположим, следующий код 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. Как это сделать? Благодарю.

1 ответ

Решение

См. Знак Расширения int в C для тесно связанного вопроса (но не дубликат).

Вы должны знать, что почти все в битовых полях "определяется реализацией". В частности, не ясно, можно ли присвоить отрицательные числа 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 спецификатор заставляет объекты символов и битовые поля быть подписанными; это избыточно в других контекстах. —Конечная записка]

§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 или long long битовое поле. Значение bool может быть успешно сохранено в битовом поле любого ненулевого размера. Оператор адреса & не должен применяться к битовому полю, поэтому указатели на битовые поля отсутствуют. Неконстантная ссылка не должна быть привязана к битовому полю (8.5.3). [Примечание: если инициализатор для ссылки типа const T& является l-значением, которое относится к битовому полю, ссылка привязывается к временному инициализированному, чтобы содержать значение битового поля; ссылка не привязана к битовому полю напрямую. Смотрите 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) его ширине предшествует двоеточие.

¶10 Битовое поле интерпретируется как имеющее целочисленный тип со знаком или без знака, состоящий из указанного количества битов. 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).

Другие вопросы по тегам