В C, как получить рассчитать отрицательное количество без знака - PullRequest
6 голосов
/ 09 ноября 2009

В книге K & R ANSI C, раздел A.7.4.5 (Унарный минус оператор) указано:

... Отрицательное число без знака вычисляется путем вычитания повышенного значения из наибольшего значения повышенного типа и добавления одного; ...

Как именно это рассчитывается? Не могли бы вы привести короткий пример C?

Я не понимаю, как это может привести к отрицательности, скажем, 200u: вычитание 200 из максимального значения любого целочисленного типа (со знаком или без знака) и добавление 1 не приводит к -200.

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

Ответы [ 6 ]

8 голосов
/ 09 ноября 2009

Беззнаковые значения не могут быть отрицательными, поэтому -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");

(обратите внимание, что мы нигде не храним результат оператора отрицания - это не проблема).

7 голосов
/ 09 ноября 2009

Очевидно, вы пропустили слово без знака в описании, которое вы цитировали. Это ключевое слово в этом случае. На языке C «отрицательный» для unsigned количества все еще остается без знака, что означает, что он на самом деле не является отрицательным. Без знака значения не могут быть отрицательными по определению. Они всегда положительны или равны 0. Арифметика беззнаковых значений в C является арифметической по модулю или, простыми словами, беззнаковые величины «оборачиваются» при выполнении арифметических операций над ними. Унарное отрицание не является исключением. Вычисление -n, когда n не подписано, ничем не отличается от вычисления 0 - n. Если n равно unsigned int и его значение равно 200, ожидаемый результат будет не -200, а скорее UINT_MAX - 200 + 1, что в точности соответствует цитате.

6 голосов
/ 09 ноября 2009

Он описывает операции для реализации модульной арифметики , то есть он вычисляет значение, такое что

a + (-a) == 0

Это заставляет отрицательное число без знака вести себя близко к отрицанному числу со знаком.

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

4 голосов
/ 09 ноября 2009

Операции над беззнаковыми целочисленными типами используют модульную арифметику. Арифметика по модулю 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 и остаток от m-k, поэтому правильный ответ - m-k.

Беззнаковые целые числа в C обычно описываются максимальным значением, а не модулем, что означает, что беззнаковое 16-битное число обычно описывается как от 0 до 65535, или как имеющее максимальное значение 65535. Это описывает значения путем указания m-1, а не m.

Какая у вас цитата говорит, что отрицательное значение берется путем вычитания его из m-1 с последующим добавлением 1, поэтому -k равно m - 1 - k + 1, то есть m - k. Описание немного оканчивается, но оно указывает правильный результат в терминах ранее существующих определений.

4 голосов
/ 09 ноября 2009

Другой вопрос уже коснулся этой темы

Пример

unsigned char i = -10;
printf("%u\n",i);

Результат

246
3 голосов
/ 09 ноября 2009

Давайте будем проще и посмотрим на символ без знака ... 8 бит с диапазоном значений 0-255.

Что такое (unsigned char) -10 и как оно рассчитывается?

Исходя из приведенного вами заявления K & R, мы имеем:

повышенное значение -10 равно 10 , вычитаемому из наибольшее значение рекламируемого типа - 255 плюс 1 = 246

Итак (без знака) -10 на самом деле 246. Это имеет смысл?

...