Заполнение битов в целых числах без знака и побитовые операции в C89 - PullRequest
17 голосов
/ 18 декабря 2010

У меня много кода, который выполняет побитовые операции над целыми числами без знака. Я написал свой код с предположением, что эти операции выполняются с целыми числами фиксированной ширины без каких-либо дополнительных битов. Например, массив 32-разрядных целых чисел без знака, из которых все 32 бита доступны для каждого целого числа.

Я пытаюсь сделать мой код более переносимым и хочу убедиться, что я C89-совместим (в данном случае). Одна из проблем, с которыми я сталкивался, - это возможные дополненные целые числа. Возьмите этот крайний пример из руководства GMP :

Однако в векторных системах Cray можно заметить, что short и int всегда хранятся в 8 байтах (и это указывает sizeof), но используют только 32 или 46 бит. Признак гвоздей может объяснить это, передав, например, 8*sizeof(int)-INT_BIT.

Я также читал об этом типе заполнения в других местах. Я на самом деле читал пост на SO вчера вечером (извините, у меня нет ссылки, и я собираюсь процитировать что-то похожее из памяти), где, если у вас есть, скажем, двойной с 60 используемыми битами, остальные 4 могли бы использоваться для заполнения, и эти биты заполнения могут служить некоторым внутренним целям, поэтому они не могут быть изменены.


Так скажем, например, мой код скомпилирован на платформе, где тип unsigned int имеет размер 4 байта, каждый байт составляет 8 бит, однако наиболее значимые 2 бита являются битами заполнения. Будет ли UINT_MAX в этом случае 0x3FFFFFFF (1073741823)?

#include <stdio.h>
#include <stdlib.h>

/* padding bits represented by underscores */
int main( int argc, char **argv )
{
    unsigned int a = 0x2AAAAAAA; /* __101010101010101010101010101010 */
    unsigned int b = 0x15555555; /* __010101010101010101010101010101 */
    unsigned int c = a ^ b; /* ?? __111111111111111111111111111111 */
    unsigned int d = c << 5; /* ??  __111111111111111111111111100000 */
    unsigned int e = d >> 5; /* ?? __000001111111111111111111111111 */

    printf( "a: %X\nb: %X\nc: %X\nd: %X\ne: %X\n", a, b, c, d, e );
    return 0;
}
  • Безопасно ли XOR два целых числа с битами заполнения?
  • Разве я не сделал бы XOR какими-либо битами заполнения?

Я не могу найти это поведение в C89.

Кроме того, гарантируется, что переменная c будет 0x3FFFFFFF или если, например, оба бита заполнения были включены в a или b, то c будет 0xFFFFFFFF?

Тот же вопрос с d и e. Я управляю битами заполнения, сдвигая? Я ожидаю увидеть это ниже, предполагая, что 32 бита с двумя старшими значащими битами, используемыми для заполнения, но я хочу знать, гарантировано ли что-то подобное:

a: 2AAAAAAA
b: 15555555
c: 3FFFFFFF
d: 3FFFFFE0
e: 01FFFFFF

Кроме того, биты заполнения всегда являются старшими значащими битами или они могут быть младшими значащими битами?


РЕДАКТИРОВАТЬ 19/12/2010 17:00 EST : Кристоф ответил на мой вопрос. Спасибо!
Я также спросил (выше), всегда ли биты заполнения являются наиболее значимыми битами. Это приводится в обосновании стандарта C99, и ответ - нет. Я играю безопасно и предполагаю то же самое для C89. Вот, в частности, что говорит обоснование C99 для §6.2.6.2 (Представление целочисленных типов):

Биты заполнения доступны для пользователя в виде целого числа без знака. Например, предположим, что машина использует пару 16-битных шорт (каждая со своим собственным знаковым битом) для создания 32-битного целого числа, а знаковый бит нижнего короткого замыкания игнорируется при использовании в этом 32-битном целом. Затем, как 32-разрядное знаковое целое, существует бит дополнения (в середине 32-разрядного), который игнорируется при определении значения 32-разрядного знакового целого. Но если этот 32-битный элемент обрабатывается как 32-битное беззнаковое целое, то этот бит дополнения виден программе пользователя. Комитету C сказали, что есть машина, которая работает таким образом, и это одна из причин, по которой биты заполнения были добавлены в C99.

В сносках 44 и 45 упоминается, что биты четности могут быть битами заполнения. Комитет не знает ни о каких машинах с доступными для пользователя битами четности в целом числе. Поэтому комитет не знает ни о каких машинах, которые обрабатывают биты четности как биты заполнения.


РЕДАКТИРОВАТЬ 28/12/2010 15:00 EST : я нашел интересное обсуждение на comp.lang.c несколько месяцев назад.

Одно замечание Дитмара, которое мне показалось интересным:

Отметим, что биты заполнения не являются необходимыми для существования представлений ловушек;комбинации битов значения, которые не представляют значение типа объекта, также подойдут.

1 Ответ

10 голосов
/ 18 декабря 2010

Побитовые операции (например, арифметические операции) работают со значениями и игнорируют заполнение.Реализация может или не может модифицировать биты заполнения (или использовать их внутренне, например, как биты четности), но переносимый C-код никогда не сможет обнаружить это.Любое значение (включая UINT_MAX) не будет включать заполнение.

Если целочисленное заполнение может привести к проблемам, это если вы используете такие вещи, как sizeof (int) * CHAR_BIT, а затем пытаетесь использовать сдвиги для доступа ко всем этим битам.Если вы хотите быть переносимым, используйте только (unsigned) char, целые числа фиксированного размера (дополнение C99) или определите количество битов значения программно.Это можно сделать во время компиляции с препроцессором, сравнив UINT_MAX со степенями 2 или во время выполнения с использованием битовых операций.

edit:

C90вообще не упоминает целочисленное заполнение, но, насколько я могу судить, «невидимые» предшествующие или завершающие биты целочисленного заполнения не должны нарушать стандарт (я не прошел все соответствующие разделы, чтобы убедиться, что это действительно так,хоть);Вероятно, существуют проблемы со смешанными битами заполнения и значения, как упомянуто в обосновании C99, поскольку в противном случае стандарт не потребовалось бы изменять.

Что касается значения, доступного для пользователя: биты заполнения доступны, посколькуВы всегда можете получить любой бит foo (включая заполнение), используя битовые операции на ((unsigned char *)&foo)[…].Будьте осторожны при изменении битов заполнения: результат не изменит значение целого числа, но, тем не менее, может создать представление ловушки.В случае C90 это неявно не указано (как в случае вообще не упомянуто), в случае C99 это определяется реализацией.

Однако это было не то, о чем шла речь в обосновании: цитируемая архитектура представляет32-разрядные целые числа через два 16-разрядных целых числа.В случае типов без знака результирующее целое число имеет 32 бита значения и точность 32;в случае целых чисел со знаком он имеет только 31 бит значения и точность 30: один из знаковых битов 16-разрядных целых чисел используется в качестве знакового бита 32-разрядного целого числа, другой игнорируется, создавая таким образомбит дополнения, окруженный битами значения.Теперь, если вы получите доступ к 32-разрядному целому числу со знаком в виде целого числа без знака (которое явно разрешено и не нарушает правила псевдонимов C99), бит дополнения становится (доступным для пользователя) битом значения.

...