Почему -(-2147483648) = - 2147483648 в 32-разрядной машине?

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

Почему -(-2147483648) = -2147483648 (по крайней мере при компиляции в C)?

Комментарии к вопросу (10)
Решение

Отрицая себя (без суффиксов) целочисленную константу:

Выражение -(-2147483648) совершенно определенными в C, однако это может быть не очевидно, почему это так.

Когда вы пишете -2147483648, образуется как оператор унарный минус применяется к целой константой. Если 2147483648 может'т быть выражена как инт, тогда она представляется как долго или длинный<суп>*</SUP-серфинг> (в зависимости от того, подходит Во-первых), причем последний тип является гарантированным C стандарт, чтобы покрыть стоимость<суп>†</SUP-серфинг>.

Чтобы подтвердить, что вы можете проверить это:

printf("%zu\n", sizeof(-2147483648));

который дает 8 на моей машине.

Следующий шаг-нанесите второй оператор-, и в этом случае окончательное значение 2147483648L (предполагая, что в конечном итоге он был представлен как "длинные"). Если вы попытаетесь присвоить его инт объект, следующим образом:

int n = -(-2147483648);

тогда фактическое поведение реализации. Ссылаясь на стандарт:

С11 §6.3.1.3/3 знаковых и беззнаковых целых чисел

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

Самый распространенный способ-это просто отсечения высших разрядов. Например, ССЗ документы Как:

для преобразования к типу ширины N, величина уменьшается по модулю 2^Н , Чтобы быть в пределах диапазона типа; никакой сигнал не поднимался.

По существу, преобразование типа ширина 32 может быть проиллюстрировано с помощью побитовой операции и:

value & (2^32 - 1) // preserve 32 least significant bits

В соответствии с два'с дополнением арифметическое, значение " N " формируется с помощью нулей и MSB (знак) набор битов, который представляет значение -2^31, то есть -2147483648.

Отрицая в инт объект:

Если вы попытаетесь отрицать инт объект, который содержит значение -2147483648, то предполагаю два's машина дополнения, программа выставки неопределенное поведение:

n = -n; // UB if n == INT_MIN and INT_MAX == 2147483647

С11 §6.5/5 выражений

если исключительное состояние возникает во время оценки выражение (То есть, если результат математически не определено или не в диапазоне представимых значений для ее типа), поведение и GT; неопределено.

Дополнительная литература:

  • [Типа int32-С. обеспечить, чтобы операции на знаковых целых чисел не приводят к переполнению][3]

<суп>*) в стандартном удалился С90, не было никакого длинный тип и правила были другие. В частности, последовательность без суффиксов десятичных был типа int, длинный инт, беззнаковый Long и int (С90 §6.1.3.2 целочисленных констант).</SUP и ГТ;

<суп>†) это связано с LLONG_MAX, который должен быть не менее +9223372036854775807 (С11 §5.2.4.2.1/1).</SUP и ГТ;

[3]: https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow#INT32-C. Ensurethatoperationsonsignedintegersdonotresultinoverflow-UnaryNegation

Комментарии (7)

Примечание: этот ответ не распространяется на устаревший стандарт ИСО С90, которые до сих пор используют многие составители

В первую очередь, на стандарте C99, C11, от выражения `-(-2147483648) == -2147483648 это на самом деле ложные:

int is_it_true = (-(-2147483648) == -2147483648);
printf("%d\n", is_it_true);

печать

0

Так как вполне возможно, что это значение true? Машина используется 32-разрядная два'с дополнением целых чисел. В 2147483648-это целочисленная константа, которая совсем не&#39;т помещается в 32 бита, таким образом, он будет либо длинный int или длинный инт в зависимости от первого, где он подходит. Этим отрицаются приведет к -2147483648 - и снова, хотя число -2147483648 может поместиться в 32-разрядное целое число, выражение -2147483648 состоит из >32-разрядное положительное целое число предшествует унарный -!

Вы можете попробовать следующую программу:

#include 

int main() {
    printf("%zu\n", sizeof(2147483647));
    printf("%zu\n", sizeof(2147483648));
    printf("%zu\n", sizeof(-2147483648));
}

Выход на такой машине скорее всего будет 4, 8 и 8.

Теперь, -2147483648 отрицается снова приводят к +214783648, который до сих пор типа длинный int или длинный Инт, и все нормально.

В С99, С11, целочисленное константное выражение -(-2147483648) четко определено, на все соответствующие реализации.


Теперь, когда это значение присваивается переменной типа " int " с 32 бит и два'ы дополнит представление, значение не может быть представлено в нем - значения на 32-бит 2'ы дополнит бы в диапазоне от -2147483648 до 2147483647.

Стандарт С11 [6.3.1.3p3](http://port70.net/~НСЗ/с/с11/n1570.сообщение: 6.3.1.3p3) говорит следующее целочисленных преобразований:

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

То есть, C стандарт не'т на самом деле определить, что значение в этом случае будет, или не'т исключает возможности, что выполнение программы останавливается из-за сигнала поднимается, но оставляет его реализации (например, компиляторов), чтобы решить, как справиться с этим [(С11 3.4.1)](http://port70.net/~НСЗ/с/с11/n1570.сообщение: 3.4.1):

реализация-определено поведение

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

и [(3.19.1)](http://port70.net/~НСЗ/с/с11/n1570.сообщение: 3.19.1p1):

реализация-определено значение

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


В вашем случае реализация-определено поведение состоит в том, что значение является 32 младший бит [*]. Из-за 2'с дополнением, значение 0x80000000 (долго) долго int значение `бит 31 установлен и все остальные биты сброшены. В 32-разрядных, два&#39;с дополнительными целыми числами со знаком бит 31-знак бит - это означает, что число отрицательное; все биты обнулены значение означает, что значение минимальное представимое число, т. е.INT_MIN`.


[*] ССЗ документы, его реализация-определено поведение в этом случае следующим образом:

результат или сигнал, поднятые, преобразование целого числа в знаковый целочисленный тип, когда значение не может быть представлено в объект этого типа (С90 6.2.1.2, С99 и С11 6.3.1.3).

для преобразования в тип ширина N, то значение уменьшается по модулю 2^n, чтобы быть в пределах диапазона типа; никакой сигнал не поднимался.

Комментарии (0)

Это не вопрос, для реализации C с 32-бит два'ы дополнит представление для типа int, эффект от применения унарного оператора отрицания к инт, имеющий значение -2147483648 является определен. То есть, язык C специально дезавуирует, обозначающий результат оценки такой операции.

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

Однако возникают небольшие проблемы, для двух чисел, которые не имеют никакой ценности биты: 0, который имеет набор битов, а число, которое имеет только бит знака устанавливается (-2147483648 в 32-разрядное представление). Когда вы перевернуть все биты либо из них, вы в конечном итоге с все значение биты. Поэтому, когда вы затем добавить 1, то в результате переполнения биты значения. Если вы представляете выполняя сложения, как если бы числа были неподписанные, лечение знаковый бит значение бит, тогда вы получите

    -2147483648 (decimal representation)
-->  0x80000000 (convert to hex)
-->  0x7fffffff (flip bits)
-->  0x80000000 (add one)
--> -2147483648 (convert to decimal)

Подобное относится и к инвертирующего ноль, но в этом случае переполнение при добавлении 1 превышает былая бит знака, тоже. Если переполнение игнорируется, в результате чего 32 младших битов равны нулю, поэтому -0 == 0.

Комментарии (4)

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

В 4-разрядное число, возможные значения от 0000 до 1111. Что бы от 0 до 15, но если ты хочешь представлять отрицательные числа, первый бит используется для указания знака (0 для положительных и 1 для отрицательных).

Так что 1111-это не 15. Как первый бит равен 1, Это'с отрицательным числом. Чтобы узнать его значение, мы используем два дополнения метод, как уже описано в предыдущем ответе: "и инвертировать биты и прибавить 1" и:

  • инвертирование битов: 0000
  • добавление 1: 0001

0001 в двоичной 1 в десятичной, так что 1111-это -1.

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

Теперь давайте's см. 1000. Первый бит равен 1, поэтому он'с отрицательным числом. Через два-дополнять метод:

  • инвертировать биты : 0111
  • добавить 1: 1000 (8 в десятичной)

Так что 1000-это -8. Если мы это сделаем -(-8), в двоичной системе это означает, - (1000), который фактически означает использование двух-дополнить способ в 1000. Как мы видели выше, результат тоже 1000. Так, в 4-разрядное число,-(-8)` равен -8.

В 32-разрядное число, -2147483648 в двоичной системе это 1000..(31 нули), но если вы используете двух-дополнить способ, вы'll итоге с тем же значением (результат один и тот же номер).

Что's, почему в 32-разрядное число -(-2147483648) равен -2147483648

Комментарии (0)

Это зависит от версии C, особенностях внедрения и говорим ли мы о переменные или литералы значения.

Первое что нужно понять, что нет никаких негативных целочисленные литералы в C "от -2147483648" это унарная операция минус последовал положительный целочисленный литерал.

Предположим, что мы бежим на обычном 32-битную платформу, где int и Long оба 32 бит и 64 бит долго долго и рассмотреть выражение.

(-(-2147483648) == -2147483648 )

Компилятор должен найти тип, который может хранить 2147483648, на comforming С99 компилятор будет использовать тип и"Длинный", но компилятор С90 можно использовать типа „неподписанных давно".

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

Комментарии (0)

По той же причине, что обмотки кассетной деки счетчик 500 шагов вперед От 000 (счет 001 002 003 ...) покажет 500, и его обмотки обратной 500 шагов назад от 000 (до 999 998 997 ...) также покажут 500.

Это два'нотации с дополнением. Конечно, с 2'подписать конвенцию с дополнением стоит считать самый верхний бит знаковый бит, то результат превышает диапазон представимых, как 2000000000+2000000000 превышает диапазон представимых.

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

Комментарии (0)