Как исправить это 5-битное объединение на основе границ в C?

У меня есть система, которая ожидает 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 22.06.2024 23:00

@zwol отследить выходные данные в том виде, в котором я их видел, практически невозможно, но причина ошибки в ядре этой системы с конкретной сборкой gcc, которую я использовал, вызывала неопределенное поведение. Считается, что причиной ошибочного поведения является использование SceUChar8 в объединении mgp5555 вместо SceUShort16.

mpo admin 22.06.2024 23:05

SceUChar8 предположительно является 8-битным типом? Какой эффект вы ожидали от его использования в данном контексте, и что произошло вместо этого? Будьте предельно конкретны. При необходимости отредактируйте сотни строк отладочной информации в своем вопросе.

zwol 22.06.2024 23:15

Похоже, вам нужен определенный битовый шаблон из вашего битового поля, но C не определяет, как упорядочиваются биты. Итак, ответ: вы не можете. Вам нужно продолжать использовать Bit Ops.

ikegami 22.06.2024 23:20

Почему вы не можете установить индивидуальные значения? void set_green(SceUShort16 *color, SceUChar8 green) { *color &= ~(0x1F << 5); *color |= green << 5; } и т. д.

dimich 22.06.2024 23:30

@zwol, используя 8-битный тип, в сумме дает 32-битные значения, а «значение» ожидает 16-битное значение. Мне удалось успешно прочитать его, несмотря на ошибку 32-битного выравнивания при использовании 8-битных значений, но для того, чтобы установите их, мне пришлось использовать метод сдвига вручную. Я думаю, что причина, по которой это работает сейчас, заключается в том, что компилятор понимает, что структура объединения формирует 16-битное значение. Вывод до этого, когда я использовал UCHAR8, был буквально настолько случайным, насколько это возможно, и под этим я имею в виду, что вывод всегда был полным мусором.

mpo admin 22.06.2024 23:59

@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 = темп; } Возврат значения.Значение; }

mpo admin 22.06.2024 23:59

Напишите свои собственные оболочки и используйте битовые операции.

KamilCuk 23.06.2024 00:07

@KamilCuk у функции теперь есть аргумент, позволяющий программисту выбирать, хочет ли он использовать метод объединения или сдвига.

mpo admin 23.06.2024 00:08
to let the programmer chose if they want to use the union or the shift method звучит как бремя обслуживания и дублирование кода. Я предпочитаю следовать соглашению о том, что существует один способ сделать определенную вещь.
KamilCuk 23.06.2024 00:10

Я отредактировал вопрос, включив в вопрос ожидаемую информацию о макете из вашего ответа. Откатитесь назад (или отредактируйте по своему желанию), если вы недовольны.

Peter - Reinstate Monica 23.06.2024 13:47
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
11
115
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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}|

потому что у вас здесь другой тип

0___________ 22.06.2024 23:05

Проблема, вероятно, заключается в том, что для первого битового поля компилятор «выделяет» объект указанного типа элемента, который в вашем неработающем примере был 8-битным целым числом. Затем он помещает туда последовательные элементы битового поля, пока объект не заполнится. Поскольку ваш объект имеет только 8 бит, второй член уже не подходит; компилятор выделяет второй символ. То же самое для членов 3 и 4: создается структура из 4 символов = 32 бита. Если вы объявите элементы как 16-битные типы, все битовые поля поместятся в первый «выделенный» объект. Союз должен быть лишним (и, конечно, раньше был бессмысленным).

Peter - Reinstate Monica 23.06.2024 13:25

Согласно 6.7.2.1, параграф 11 (проекта) стандарта C11: «Реализация может выделить любую адресуемую единицу хранения, достаточно большую для хранения битового поля. Если остается достаточно места, битовое поле, которое сразу следует за другим битом -поле в структуре должно быть упаковано в соседние биты одной и той же единицы. Если остается недостаточно места, то, помещается ли битовое поле, которое не помещается в следующую единицу, или перекрывает соседние единицы, определяется реализацией...

Andrew Henle 23.06.2024 19:15

(продолжение) «Порядок распределения битовых полей внутри единицы (от старшего к младшему или от младшего к старшему) определяется реализацией. Выравнивание адресуемой единицы хранения не указано». Если вы всегда хотите контролировать расположение битов в ваших переменных, ВЫ НЕ МОЖЕТЕ ИСПОЛЬЗОВАТЬ БИТОВЫЕ ПОЛЯ.

Andrew Henle 23.06.2024 19:16
Ответ принят как подходящий

Как исправить это 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, основной проблемой являются проблемы с порядком битов/байтов, которые должен решать код, предназначенный для более чем одной платформы. Но ваше предложение бессмысленно: если вы выполняете упаковку вручную, битовые поля больше не нужны.

Peter - Reinstate Monica 23.06.2024 13:30
they are designed exactly for the OP's purpose И да, и нет. Они предназначены для представления структуры машины, на которой вы программируете. Итак, порядок байтов зависит от машины, на которой вы находитесь. Таким образом, если вы взаимодействуете с DMA на машине с прямым порядком байтов или с прямым порядком байтов, структура битового поля может быть одинаковой (при условии, что макеты DMA следуют за машиной). Во многих случаях люди используют поля bif для записи интерфейсов для внешних устройств, расположение битов которых указано в таблицах данных, не связанных с архитектурой, на которой вы пишете свою программу. В таких случаях следует использовать битовые сдвиги.
KamilCuk 23.06.2024 13:35
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..
KamilCuk 23.06.2024 13:37

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