C: поведение унарного минуса с неподписанными операндами
Кажется, я не могу найти соответствующие части в стандарте C, полностью определяющие поведение унарного минусового оператора с беззнаковыми операндами.
Стандарт C++ 2003 года (да, C++, потерпите несколько строк) говорит в 5.3.1c7: The negative of an unsigned quantity is computed by subtracting its value from 2^n, where n is the number of bits in the promoted operand.
Стандарт C 1999 года, однако, не содержит такого явного утверждения и не дает четкого определения унарного поведения ни в 6.5.3.3c1,3, ни в 6.5c4. В последнем это говорит Some operators (the unary operator ~, and the binary operators <<, >>, &, ^, and |, ...) ... return values that depend on the internal representations of integers, and have implementation-defined and undefined aspects for signed types.)
, что исключает унарный минус и вещи, похоже, остаются расплывчатыми.
Этот предыдущий вопрос относится к книге K&R ANSI C, раздел A.7.4.5, в которой говорится The negative of an unsigned quantity is computed by subtracting the promoted value from the largest value of the promoted type and adding one
,
Что будет стандартом C 1999 года, эквивалентным приведенной выше цитате из книги?
6.2.5c9 говорит: A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.
Это оно? Или я что-то пропустил?
3 ответа
Поведение унарного оператора минус на беззнаковых операндах не имеет ничего общего с тем, использует ли машина арифметику с двумя дополнительными числами со знаковыми числами. Вместо этого, учитывая unsigned int x,y;
заявление y=-x;
вызовет y
чтобы получить любую ценность, которую он должен был бы держать, чтобы сделать x+y
равен нулю. Если x
ноль, y
также будет ноль. Для любого другого значения x
, это будет UINT_MAX-x+1
в этом случае арифметическое значение x+y
будет UINT_MAX+1+(y-y)
который, когда назначен на unsigned integer
, буду иметь UINT_MAX+1
вычесть из него, давая ноль.
В каждой реализации, о которой я знаю, отрицание рассчитывается как дополнение к двум...
int a = 12;
int b = -a;
int c = ~a + 1;
assert(b == c);
... так что на самом деле нет никакой физической разницы между отрицательными знаковыми и "отрицательными" целыми числами без знака - единственная разница в том, как они интерпретируются.
Так что в этом примере...
unsigned a = 12;
unsigned b = -a;
int c = -a;
... b
а также c
собираются содержать одинаковые биты. Единственная разница в том, что b
интерпретируется как 2^32-12 (или 2^64-12), а c
интерпретируется как "нормальный" -12.
Таким образом, отрицательный рассчитывается точно так же, независимо от "подписи", и приведение между неподписанным и подписанным фактически не допускается (и никогда не может вызвать переполнение в том смысле, что некоторые биты должны быть "обрезаны"). -off ").
Уже поздно, но все равно...
C заявляет (довольно жестко, как уже упоминалось в других ответах), что
любой беззнаковый тип - это двоичное представление с определенным для типа количеством бит
все арифметические операции с беззнаковыми типами выполняются (mod 2^N), "mod" - математическое определение модуля, а "N" - количество битов, используемых для представления типа.
Оператор унарного минуса, применяемый к беззнаковому типу, ведет себя так, как если бы значение было повышено до следующего более крупного подписанного типа, затем отрицается, а затем снова преобразуется в беззнаковый и усекается до исходного типа. (Это небольшое упрощение, поскольку целочисленное продвижение происходит для всех типов, которые имеют меньше битов, чем int, но я думаю, что это достаточно близко.)
Некоторые компиляторы действительно выдают предупреждения при применении унарного минуса к беззнаковому типу, но это просто для пользы программиста. IMHO конструкция четко определена и переносима.
Но если сомневаетесь, просто не используйте унарный минус: напишите "0u - x" вместо "-x", и все будет хорошо. Любой достойный генератор кода создаст из этого просто отрицательную инструкцию, если оптимизация не будет полностью отключена.