Вы всегда можете реализовать перенос на любую ширину вручную, например a++; a&=0x7fffffff;
, чтобы замаскировать результат до 31 бита и реализовать беззнаковый 31-битный тип. Возвращение расширения знака к более широкому типу обходится дороже, обычно смещение влево, а не арифметическое смещение вправо, если ширина источника не поддерживается специально языком и / или оборудованием. (например, ARM имеет команду расширения со знаком битового поля, которая может извлекать и расширять знак произвольного битового поля в регистр полного целого числа).
Есть процессоры со словами и / или байтами, которые не кратны 8 битам, например PDP-10 имеет 36-битные слова. https://en.wikipedia.org/wiki/36-bit. В этой системе естественный размер составляет 36 бит, и 32 бита будут нестандартным типом, требующим дополнительных инструкций.
Могу ли я иметь какие-то структуры данных, которые будут храниться в памяти, например, 31 бит -> 31 бит -> 31 бит, и можно ли заставить процессор работать с ними как 31 бит.
Нет, вы не можете этого сделать. Нет процессоров, о которых мне известно, с битно-адресуемой памятью. Любая загрузка / сохранение должна быть выровнена по крайней мере до границ байтов. (Байт-адресуемая память в наши дни почти универсальна, но некоторые DSP и некоторые старые процессоры, такие как DEC Alpha, имеют / имеют только адресную память).
C с битовыми полями будет эмулировать более узкие типы, но с заполнением; вы не можете избежать того, что сгенерированный компилятором ассемблер коснется отступа.
, например
struct i31 {
int i:31; // note *signed* int
// 1 bit of padding is implicit on targets with 32-bit int
};
struct i31 inc(struct i31 x) {
x.i++;
return x;
}
int extend_to_int(struct i31 x) {
return x.i;
}
компилируется для x86-64 в это (в проводнике компилятора Godbolt) .
Вероятно, я должен был бы использовать gcc -fwrapv
, чтобы определить поведение переполнения со знаком в качестве дополнения 2. Я не уверен, что правила C для битовых полей, является ли присвоение подписанного результата подписанному битовому полю все еще вызывает поведение переполнения подписи undefined-поведение в ISO C и C ++.
# gcc8.2 -O3
inc(i31):
lea eax, [rdi+1]
and edi, -2147483648 # keep the top bit of the input
and eax, 2147483647 # keep the low 31 bits of i++
or eax, edi # merge.
# IDK why it can't / doesn't just leave the carry-out in the padding
ret
extend_to_int(i31):
lea eax, [rdi+rdi] # left shift by 1 (and copy)
sar eax # shift arithmetic right (by 1)
ret
Но ARM аккуратен и имеет лучшие инструкции по битовым полям, чем x86. (Практически все имеют лучшие инструкции битового поля, чем x86).
# ARM gcc7.2 -march=armv8-a -O3
inc(i31):
add r3, r0, #1
bfi r0, r3, #0, #31 # bitfield insert to preserve the high bit of the struct
bx lr
extend_to_int(i31):
sbfx r0, r0, #0, #31 # signed bitfield extract
bx lr