Является ли вычитание целого числа без знака определенным поведением?



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



unsigned int To, Tf;

To = getcounter();
while (1) {
Tf = getcounter();
if ((Tf-To) >= TIME_LIMIT) {
break;
}
}


Это единственная смутно соответствующая цитата из стандарта C, которую я мог найти.




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




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



т. е.



0x0000-0x0001 = = 0x 1 0000-0x0001 == 0xFFFF



в отличие от использования семантики со знаком, зависящей от реализации:



0x0000-0x0001 = = (без знака)(0 + -1) = = (0xFFFF но также 0xFFFE или 0x8001)



какая или какая интерпретация правильная? Это вообще определено?

617   4  

4 ответов:

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

  1. [...] Вычисление с участием беззнаковых операндов никогда не может переполняться, поскольку результат, который не может быть представлен результирующим целочисленным типом без знака, является уменьшено по модулю число, которое на единицу больше наибольшего значения, которое может быть представлен результирующим типом. (ISO / IEC 9899: 1999 (E) §6.2.5 / 9)

Как вы вижу, (unsigned)0 - (unsigned)1 равно -1 по модулю UINT_MAX+1, или другими словами, UINT_MAX.

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

когда вы работаете с без подписи типы модулярной арифметики (также известный как "обтекать" поведение) осуществляется. Чтобы понять это модулярной арифметики, просто взгляните на эти часы:

enter image description here

9 + 4 = 1 (13 mod 12), так что в другую сторону это:1 - 4 = 9 (-3 mod 12). Тот же принцип применяется и при работе с беззнаковыми типами. Если результат и unsigned, то модульная арифметика имеет место.


Теперь рассмотрим следующие операции хранения результата в виде unsigned int:

unsigned int five = 5, seven = 7;
unsigned int a = five - seven;      // a = (-2 % 2^32) = 4294967294 

int one = 1, six = 6;
unsigned int b = one - six;         // b = (-5 % 2^32) = 4294967291

когда вы хотите убедиться, что результат signed, а затем хранил его в signed переменная или приведите ее к signed. Когда вы хотите получить разницу между числами и убедитесь, что модульная арифметика не будет применяться, то вы должны рассмотреть возможность использования abs()

Ну, первая интерпретация верна. Однако ваши рассуждения о" знаковой семантике " в этом контексте неверны.

опять же, ваша первая интерпретация верна. Беззнаковая арифметика следуйте правилам арифметики по модулю, что означает 0x0000 - 0x0001 значение 0xFFFF для 32-разрядных типов без знака.

однако для получения того же результата требуется и вторая интерпретация (основанная на "знаковой семантике"). Т. е. даже если вы оцениваете 0 - 1 in домен подписанного типа и получить -1 как промежуточный результат, это -1 по-прежнему требуется производить 0xFFFF когда позже он преобразуется к типу unsigned. Даже если какая-то платформа использует экзотическое представление для целых чисел со знаком (Дополнение 1, знаковая величина), эта платформа все равно должна применять правила арифметики по модулю при преобразовании целочисленных значений со знаком в беззнаковые.

например, эта оценка

signed int a = 0, b = 1;
unsigned int c = a - b;

еще гарантированно производить UINT_MAX на c, даже если платформа использует экзотическое представление для целых чисел со знаком.

С беззнаковыми числами типа unsigned int или больше, при отсутствии преобразования типа a-b определяется как дающее беззнаковое число, которое при добавлении к b даст a. Преобразование отрицательного числа в беззнаковое определяется как получение числа, которое при добавлении к исходному числу, обратному знаку, даст ноль (поэтому преобразование -5 в беззнаковое даст значение, которое при добавлении к 5 даст ноль).

обратите внимание, что беззнаковые числа меньше, чем unsigned int может получить повышение до типа int перед вычитанием, поведение a-b будет зависеть от размера int.

Comments

    Ничего не найдено.