Подтвердить что ты не робот

Является ли арифметическое переполнение эквивалентным по модулю операции?

Мне нужно выполнить арифметику по модулю 256 в C. Так что я могу просто сделать

unsigned char i;
i++;

вместо

int i;
i=(i+1)%256;
4b9b3361

Ответ 1

Нет. Нет ничего, что гарантировало бы, что unsigned char имеет восемь бит. Используйте uint8_t от <stdint.h>, и вы будете в полном порядке. Для этого требуется реализация, которая поддерживает stdint.h: любой компилятор, совместимый с C99, но более старые компиляторы могут не предоставлять его.

Примечание: арифметика без знака никогда не переполняется и ведет себя как "modulo 2 ^ n". Поднятые арифметические переполнения с поведением undefined.

Ответ 2

Да, поведение обоих ваших примеров одинаковое. См. C99 6.2.5 §9:

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

Ответ 3

Скорее всего, да, но причины этого в этом случае на самом деле довольно сложны.

unsigned char i = 255;
i++;

i++ эквивалентно i = я + 1.

(Ну, почти. i++ значение i до того, как оно было увеличено, поэтому оно действительно эквивалентно (tmp=i; я = я + 1; tmp). Но поскольку в этом случае результат отбрасывается, это не не ставить никаких дополнительных вопросов.)

Поскольку тип unsigned char является узким типом, операнд unsigned char для оператора + переводится в int (при условии, что int может содержать все возможные значения в диапазоне unsigned char). Так что, если i == 255 и UCHAR_MAX == 255, то результат сложения равен 256 и имеет тип (со знаком) int.

Присвоение неявно преобразует значение 256 из int обратно в unsigned char. Преобразование в тип без знака хорошо определено; результат уменьшается по модулю MAX+1, где MAX - максимальное значение целевого типа без знака.

Если бы i был объявлен как unsigned int:

unsigned int i = UINT_MAX;
i++;

не было бы преобразования типов, но семантика оператора + для типов без знака также указывает модуль сокращения MAX+1.

Имейте в виду, что значение, присвоенное i, математически эквивалентно (i+1) % UCHAR_MAX. UCHAR_MAX обычно 255, и гарантированно будет по крайней мере 255, но юридически он может быть больше.

UCHAR_MAX существовать экзотическая система, в которой UCHAR_MAX тоже должен храниться в подписанном объекте int. Для этого потребуется UCHAR_MAX > INT_MAX, что означает, что система должна иметь как минимум 16-битные байты. В такой системе продвижение должно быть от unsigned char до unsigned int. Окончательный результат будет таким же. Вы вряд ли столкнетесь с такой системой. Я думаю, что есть реализации C для некоторых DSP, у которых байты больше, чем 8 бит. Количество битов в байте определяется параметром CHAR_BIT, определенным в <limits.h>.

CHAR_BIT > 8 не обязательно подразумевает UCHAR_MAX > INT_MAX. Например, у вас может быть CHAR_BIT == 16 и sizeof (int) == 2 то есть 16-битные байты и 32-битные int s).

Ответ 4

unsigned char c = UCHAR_MAX;
c++;

В принципе да, переполнения нет, но не потому, что c имеет неподписанный тип. Существует скрытое продвижение c to int здесь и целочисленное преобразование от int до unsigned char, и оно отлично определено.

Например,

 signed char c = SCHAR_MAX;
 c++;

также не имеет поведения undefined, потому что он фактически эквивалентен:

c = (int) c + 1;

и преобразование из int в signed char определено здесь (см. c99, 6.3.1.3p3 для целых преобразований). Для упрощения CHAR_BIT == 8 предполагается.

Для получения дополнительной информации о примере выше, я предлагаю прочитать это сообщение:

"Маленькая функция C из ада"

http://blog.regehr.org/archives/482

Ответ 5

Существует еще одна альтернатива, о которой не упоминалось, если вы не хотите использовать другой тип данных.

unsigned int i;
// ...
i = (i+1) & 0xFF; // 0xFF == 255

Это работает, потому что modulo element == 2^n, что означает, что диапазон будет [0, 2^n-1], и поэтому битмаска будет легко сохранять значение в пределах вашего желаемого диапазона. Возможно, этот метод был бы не намного или менее эффективен, чем версия unsigned char/uint8_t, либо в зависимости от того, какое волшебство ваш компилятор делает за кулисами, и как целевая система обрабатывает несловные нагрузки (например, некоторые Архитектура RISC требует дополнительных операций для загрузки значений, отличных от слов). Это также предполагает, что ваш компилятор не будет определять использование аргумента power-of-two по модулю арифметики на значения без знака и, конечно же, заменит вам битовую маску, так как в таких случаях использование по модулю будет иметь большее семантическое значение (хотя использование этого поскольку основа для вашего решения, конечно, не является переносимой).

Преимущество этого метода состоит в том, что вы можете использовать его для полномочий двух, которые также не являются размером типа данных, например.

i = (i+1) & 0x1FF; // i %= 512
i = (i+1) & 0x3FF; // i %= 1024
// etc.

Ответ 6

Это должно работать нормально, потому что оно должно просто переполняться до 0. Как было указано в комментарии к другому ответу, вы должны сделать это только тогда, когда значение не указано, так как вы можете получить поведение undefined с подписанным значение.

Скорее всего, лучше оставить это, используя modulo, потому что код будет лучше понят другими людьми, поддерживающими код, и интеллектуальный компилятор может в любом случае делать эту оптимизацию, что может сделать его бессмысленным в первую очередь. Кроме того, разница в производительности, вероятно, будет настолько мала, что в первую очередь это не имеет значения.

Ответ 7

Он будет работать, если количество бит, которое вы используете для представления числа, равно числу битов в двоичном (без знака) представлении (100000000) делителя -1 который в этом случае равен: 9-1 = 8 (char)