У меня есть система, которая ожидает 16-битное значение цвета с прямым порядком байтов, три цвета по 5 бит каждый и один альфа-бит.
С прямым порядком байтов это выглядит так:
R:{00000-11111} G:{00000-11111} B:{00000-11111} A:{0-1}
Я попытался смоделировать это с помощью
typedef union {
struct {
SceUChar8 R;
SceUChar8 G;
SceUChar8 B;
SceUChar8 A;
} components;
SceUInt32 Value;
} mgpColor8888;
typedef union {
struct {
SceUChar8 R: 5;
SceUChar8 G: 5;
SceUChar8 B: 5;
SceUChar8 A: 1;
} components;
SceUShort16 Value;
} mgpColor5555;
Но mgp5555 почему-то глючит, так как мне нужно объединение для создания 16-битного типа.
typedef union {
struct {
>>5 bits<< R: 5;
>>5 bits<< G: 5;
>>5 bits <<B: 5;
>>1 bits << A: 1;
} components;
SceUShort16 Value;
} mgpColor5555;
Я ударился о кирпичную стену.
Удаление кода работает, поскольку я устанавливаю 16-битное значение вручную вместо использования объединений, как мне хочется:
SceUShort16 gen5551( SceUChar8 R, SceUChar8 G, SceUChar8 B, SceUChar8 A) {
mgpColor5555 val;
R >>=3;
G >>=3;
B >>=3;
A= (A >= 0x80) ? 0b01 : 0b00;
SceUShort16 temp = ((A & 1) << 15) |((B & 0x1F) <<10 ) | ((G & 0x1F) << 5)| (R & 0x1F);
val.Value = temp;
return val.Value;
}
Почему union mgpColor5555
выше не работает?
@zwol отследить выходные данные в том виде, в котором я их видел, практически невозможно, но причина ошибки в ядре этой системы с конкретной сборкой gcc, которую я использовал, вызывала неопределенное поведение. Считается, что причиной ошибочного поведения является использование SceUChar8 в объединении mgp5555 вместо SceUShort16.
SceUChar8 предположительно является 8-битным типом? Какой эффект вы ожидали от его использования в данном контексте, и что произошло вместо этого? Будьте предельно конкретны. При необходимости отредактируйте сотни строк отладочной информации в своем вопросе.
Похоже, вам нужен определенный битовый шаблон из вашего битового поля, но C не определяет, как упорядочиваются биты. Итак, ответ: вы не можете. Вам нужно продолжать использовать Bit Ops.
Почему вы не можете установить индивидуальные значения? void set_green(SceUShort16 *color, SceUChar8 green) { *color &= ~(0x1F << 5); *color |= green << 5; }
и т. д.
@zwol, используя 8-битный тип, в сумме дает 32-битные значения, а «значение» ожидает 16-битное значение. Мне удалось успешно прочитать его, несмотря на ошибку 32-битного выравнивания при использовании 8-битных значений, но для того, чтобы установите их, мне пришлось использовать метод сдвига вручную. Я думаю, что причина, по которой это работает сейчас, заключается в том, что компилятор понимает, что структура объединения формирует 16-битное значение. Вывод до этого, когда я использовал UCHAR8, был буквально настолько случайным, насколько это возможно, и под этим я имею в виду, что вывод всегда был полным мусором.
@dimich, я могу установить их сейчас, исправление заключалось в использовании USHORT16 в объединении 5551. ``` SceUShort16 gen5551( SceUChar8 R, SceUChar8 G, SceUChar8 B, SceUChar8 A, int useUnion) { useUnion = 1; mgpColor5551 val = { .comComponents.R = R >> 3, .comComponents.G = G >> 3, .comComponents.B = B >> 3, .comComponents.A = ((A >= 0x80) ? 0b01: 0b00 ) } ; если ( !useUnion) { R >>=3; Г >>=3; Б >>=3; А= (А >= 0x80) ? 0b01: 0b00; SceUShort16 temp = ((A & 1) << 15) |((B & 0x1F) << 10 ) | ((G & 0x1F) << 5)| (Р & 0x1F); val.Value = темп; } Возврат значения.Значение; }
Напишите свои собственные оболочки и используйте битовые операции.
@KamilCuk у функции теперь есть аргумент, позволяющий программисту выбирать, хочет ли он использовать метод объединения или сдвига.
to let the programmer chose if they want to use the union or the shift method
звучит как бремя обслуживания и дублирование кода. Я предпочитаю следовать соглашению о том, что существует один способ сделать определенную вещь.
Я отредактировал вопрос, включив в вопрос ожидаемую информацию о макете из вашего ответа. Откатитесь назад (или отредактируйте по своему желанию), если вы недовольны.
typedef union {
struct {
SceUShort16 R: 5;
SceUShort16 G: 5;
SceUShort16 B: 5;
SceUShort16 A: 1;
} components;
SceUShort16 Value;
} mgpColor5555;
Это работает так, как ожидалось. До этого я использовал UCHAR8 вместо USHORT16.
Мне нужно разобраться во внутреннем устройстве, почему это работает, а UCHAR8 — нет. Поскольку я в основном исправил это с помощью грубой силы/пробуя разные типы
система ожидает 16-битное значение цвета с прямым порядком байтов.
с прямым порядком байтов это выглядит так:
Р:{00000-11111} Г:{00000-11111} Б:{00000-11111} А:{0-1}|
потому что у вас здесь другой тип
Проблема, вероятно, заключается в том, что для первого битового поля компилятор «выделяет» объект указанного типа элемента, который в вашем неработающем примере был 8-битным целым числом. Затем он помещает туда последовательные элементы битового поля, пока объект не заполнится. Поскольку ваш объект имеет только 8 бит, второй член уже не подходит; компилятор выделяет второй символ. То же самое для членов 3 и 4: создается структура из 4 символов = 32 бита. Если вы объявите элементы как 16-битные типы, все битовые поля поместятся в первый «выделенный» объект. Союз должен быть лишним (и, конечно, раньше был бессмысленным).
Согласно 6.7.2.1, параграф 11 (проекта) стандарта C11: «Реализация может выделить любую адресуемую единицу хранения, достаточно большую для хранения битового поля. Если остается достаточно места, битовое поле, которое сразу следует за другим битом -поле в структуре должно быть упаковано в соседние биты одной и той же единицы. Если остается недостаточно места, то, помещается ли битовое поле, которое не помещается в следующую единицу, или перекрывает соседние единицы, определяется реализацией...
(продолжение) «Порядок распределения битовых полей внутри единицы (от старшего к младшему или от младшего к старшему) определяется реализацией. Выравнивание адресуемой единицы хранения не указано». Если вы всегда хотите контролировать расположение битов в ваших переменных, ВЫ НЕ МОЖЕТЕ ИСПОЛЬЗОВАТЬ БИТОВЫЕ ПОЛЯ.
Как исправить это 5-битное объединение на основе границ в C?
Битовые поля не являются переносимыми. Используйте битовые операции. Напишите свои собственные оболочки, сеттеры и геттеры для доступа к 5-битным полям, которые вы хотите получить.
Я бы сделал что-нибудь вместе:
struct { uint8_t R, G, B, A; } mgpColor8888;
struct { uint8_t R :5, G :5, B :5, A :1; } mgpColor5555;
#define mgpColor5555_INIT(R, G, B, A) (struct mgpColor5555){ \
R>>3, G>>3, B>>3, A>=0x80 }
uint16_t mgpColor5555_to_16(const struct mgpColor5555 *t) {
return ((t->A & 1) << 15) |((t->B & 0x1F) <<10 ) | ((t->G & 0x1F) << 5)| (t->R & 0x1F);
}
Хотя они непереносимы, они разработаны именно для цели ОП: вписать данные в навязанный извне формат. Для этого указывается, что битовые поля являются соседними; IIUC, основной проблемой являются проблемы с порядком битов/байтов, которые должен решать код, предназначенный для более чем одной платформы. Но ваше предложение бессмысленно: если вы выполняете упаковку вручную, битовые поля больше не нужны.
they are designed exactly for the OP's purpose
И да, и нет. Они предназначены для представления структуры машины, на которой вы программируете. Итак, порядок байтов зависит от машины, на которой вы находитесь. Таким образом, если вы взаимодействуете с DMA на машине с прямым порядком байтов или с прямым порядком байтов, структура битового поля может быть одинаковой (при условии, что макеты DMA следуют за машиной). Во многих случаях люди используют поля bif для записи интерфейсов для внешних устройств, расположение битов которых указано в таблицах данных, не связанных с архитектурой, на которой вы пишете свою программу. В таких случаях следует использовать битовые сдвиги.
there is no need for the bit-fields any longer.
Согласен, но в этом случае сборка должна выглядеть достаточно красиво. Для скорости я бы рассмотрел возможность удаления битовых полей и, возможно, использования uint_fast8_t
, но я оставил битовые поля в своем ответе. | Также вы можете проверить open-std.org/jtc1/sc22/wg14/www/docs/n897.pdf страницу 64 и проверить bell-labs.com/usr/dmr/www/chist.html..
Что такого, что сделал этот код, отличалось от того, что вы ожидали? «Очевидно, это ошибка» - это недостаточно информации для меня, чтобы понять, что здесь пошло не так, и для меня это выглядит правильно, вплоть до обязательного предупреждения о том, что битовые поля недостаточно определены и на них нельзя полагаться, чтобы они соответствовали требованиям к расположению внешних данных.