В С, как получить рассчитать отрицательное количество без знака
В книге K&R ANSI C, раздел A.7.4.5 (Унарный минус оператор) указано:
... Отрицательное число без знака рассчитывается путем вычитания повышенного значения из наибольшего значения повышенного типа и добавления одного; ...
Как именно это рассчитывается? Не могли бы вы привести короткий пример C?
Я не понимаю, как это может привести к отрицательности, скажем, 200u: вычитание 200 из максимального значения любого целого типа (со знаком или без знака) и добавление 1 не приводит к -200.
Я знаю, что делает унарный минус - проблема в том, что я не вижу, как результат рассчитывается в соответствии с описанием.
6 ответов
Видимо, вы пропустили слово без знака в приведенном вами описании. Это ключевое слово в этом случае. На языке C "отрицательный" из числа без знака все еще остается без знака, что означает, что он на самом деле не является отрицательным. Без знака значения не могут быть отрицательными по определению. Они всегда положительны или равны 0. Арифметика беззнаковых значений в C является арифметической по модулю или, простыми словами, беззнаковые величины "оборачиваются" при выполнении арифметических операций над ними. Унарное отрицание не является исключением. расчета -n
когда n
без знака ничем не отличается от расчета 0 - n
, Если n
является unsigned int
и его ценность 200
ожидаемый результат не -200
, скорее UINT_MAX - 200 + 1
, что именно то, что цитата говорит вам.
Беззнаковые значения не могут быть отрицательными, поэтому -200 не является возможным результатом.
Это говорит о том, что если UINT_MAX
это 65535 в вашей системе, то результат:
unsigned a = 200;
unsigned b = -a;
long c = -a;
оставит 65336 в обоих b
а также c
,
Если ваша система имеет UINT_MAX > LONG_MAX
(как правило, потому что int
а также long
одинакового размера), вам нужно будет использовать long long
за c
(хотя обратите внимание, что нет даже никаких гарантий, что это достаточно долго).
Эта деталь (то, что результатом отрицания числа без знака является другое, обязательно положительное число без знака) может привести к некоторым неожиданным эффектам, если вы его не понимаете. Например, в этом коде первый пример печатает "true"
но второй пример печатает "false"
:
int a = 200;
unsigned b = 200;
if (-a < 100) printf("true\n"); else printf("false\n");
if (-b < 100) printf("true\n"); else printf("false\n");
(обратите внимание, что мы нигде не храним результат оператора отрицания - это не проблема).
Он описывает операции для реализации модульной арифметики, то есть он вычисляет значение так, что
a + (-a) == 0
Это заставляет отрицательное число без знака вести себя близко к отрицанному числу со знаком.
На машинах, где представление чисел является дополнением до двух (например, x86), это делается путем простой обработки битовой комбинации чисел без знака как обычного числа со знаком и использования стандартной машинной инструкции "отрицание".
Операции над целыми типами без знака используют модульную арифметику. Арифметика по модулю m во многом совпадает с обычной арифметикой, за исключением того, что результатом является положительный остаток при делении на m, если вы не сталкивались с ним в школе (более подробно см. Статью в Википедии. Например, 7–3 по модулю 10 -4, в то время как 3 - 7 по модулю 10 - 6, так как 3 - 7 - это -4, а деление его на 10 дает коэффициент -1 и остаток 6 (его также можно выразить с помощью коэффициента 0 и остаток от -4, но это не так, как это работает в модульной арифметике.) Возможные целочисленные значения по модулю m - это целые числа от 0 до m-1 включительно. Отрицательные значения невозможны, и -200 не является действительным без знака ценность при любых обстоятельствах.
Теперь унарный минус означает отрицательное число, которое не является действительным значением по модулю m. В этом случае мы знаем, что это между 0 и m-1, потому что мы начинаем с целого числа без знака. Поэтому мы смотрим на деление -k на m. Так как одно возможное значение - это коэффициент 0 и остаток от -k, другое возможное - это коэффициент -1 и остаток от mk, поэтому правильный ответ - mk.
Целые числа без знака в C обычно описываются максимальным значением, а не модулем, что означает, что 16-разрядное число без знака обычно описывается как от 0 до 65535, или как имеющее максимальное значение 65535. Это описывает значения путем указания м-1, а не м.
То, что вы цитируете, говорит, что отрицательное значение берется путем вычитания его из m-1 с последующим добавлением 1, поэтому -k равно m - 1 - k + 1, то есть m-k. Описание немного оканчивается, но оно указывает правильный результат в терминах ранее существующих определений.
Давайте будем проще и посмотрим на символ без знака... 8 бит с диапазоном значений 0-255.
Что такое (unsigned char)-10 и как оно рассчитывается?
Исходя из приведенного вами заявления K & R, мы имеем:
повышенное значение -10 равно 10, вычтено из наибольшего значения повышенного типа: 255 плюс 1 = 246
так (без знака) -10 на самом деле 246. Это имеет смысл?