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

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

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

62
задан 27 February 2017 в 04:57

6 ответов

Отрицание (неснабженной суффиксом) целочисленной константы:

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

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

, Чтобы подтвердить, что, Вы могли исследовать его:

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

, который уступает 8 на моей машине.

следующий шаг должен применяться второй - оператор, в этом случае окончательное значение 2147483648L (предполагающий, что это было в конечном счете представлено как [1 113]). При попытке присвоить его [1 114] объект, следующим образом:

int n = -(-2147483648);

затем фактическое поведение определено реализацией . Что касается Стандарта:

Целые числа со знаком C11 В§6.3.1.3/3 и целые числа без знака

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

наиболее распространенным способом являются только к сокращению более высокие биты. Например, GCC документы это как:

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

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

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

В соответствии с [1 128] дополнение two арифметика, значение [1 115] формируется со всеми нулями и MSB (знак) набор битов, который представляет значение [1 116], который является -2147483648.

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

, При попытке инвертировать int объект, который содержит значение [1 120], затем принимая дополнительную машину two, программа покажет неопределенное поведение :

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

Выражения C11 В§6.5/5

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

Дополнительные ссылки:

<час>

<глоток> *) В Стандарте withdrawed C90, не было никакого long long, тип и правила отличались. А именно, упорядочьте для неснабженного суффиксом десятичного числа, был int, long int, unsigned long int (Целочисленные константы C90 В§6.1.3.2).

<глоток> вЂ) Это происходит из-за [1 125], который должен быть по крайней мере [1 126] (C11 В§5.2.4.2.1/1).

74
ответ дан 31 October 2019 в 14:14

Примечание: этот ответ не применяется как таковой на устаревший стандарт ISO C90, который все еще используется многими компиляторами

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

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

печать

0
<час>

Поэтому, как возможно, что это оценивает к истинному? Машина использует 32-разрядный дополнение two целые числа. Эти 2147483648 целочисленная константа, которая вполне не помещается в 32 бита, таким образом это будет или long int или long long int в зависимости от того, какой бы ни является первым, где это соответствует. Отрицаемый приведет к -2147483648 - и снова, даже при том, что номер -2147483648 может поместиться в 32-разрядное целое число, выражение -2147483648 состоит из a> 32-разрядного положительного целого числа, которому предшествуют с унарным -!

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

#include <stdio.h>

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

вывод на такой машине по всей вероятности был бы 4, 8 и 8.

Теперь, -2147483648 отрицаемый снова приведет к [1 112], который имеет все еще тип long int или long long int, и все прекрасно.

В C99, C11, выражение -(-2147483648) целочисленной константы четко определено на всех реализациях приспосабливания.

<час>

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

в стандарте C11 6.3.1.3p3 говорится что следующее целочисленных преобразований:

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

таким образом, стандарт C на самом деле не определяет то, чем значение в этом случае было бы или не устраняет возможность, что осуществление программы останавливается из-за повышаемого сигнала, но предоставляет реализациям право (т.е. компиляторы) решать, как обработать его (C11 3.4.1) :

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

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

и (3.19.1) :

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

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

<час>

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

<час>

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

результат, или сигнал, повышенный, преобразовывая целое число в целое число со знаком, вводит, когда значение не может быть представлено в объекте того типа (C90 6.2.1.2, C99 и C11 6.3.1.3).

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

16
ответ дан 31 October 2019 в 14:14

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

Рассматривают в более общем плане, однако, как унарное - оператор определяется в дополнительной арифметике two: инверсия положительного числа x формируется путем зеркального отражения всех битов его двоичного представления и добавления 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.

5
ответ дан 31 October 2019 в 14:14

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

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

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

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

0001 в двоичном файле 1 в десятичном числе, таким образом 1111-1.

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

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

  • инвертируют биты: 0111
  • добавляют 1: 1000 (8 в десятичном числе)

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

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

Вот почему в 32-разрядном номере -(-2147483648), равняется -2147483648

1
ответ дан 31 October 2019 в 14:14

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

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

Позволяет, предполагают, что мы работаем на типичной 32-разрядной платформе, где интервал и долго составляет и 32 бита, и долго долго 64 бита, и рассмотрите выражение.

(-(-2147483648) ==-2147483648)

компилятор должен найти тип, который может содержать 2147483648, на компиляторе comforming C99 он будет использовать тип "долго долго", но компилятор C90 может использовать тип, "неподписанный длинный".

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

0
ответ дан 31 October 2019 в 14:14

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

Это - дополнительная нотация two. Конечно, с тех пор 2's дополнительная конвенция знака состоит в том, чтобы считать самый верхний бит знаковым битом, результат переполняет представимого диапазона, точно так же, как 2000000000+2000000000 переполнения представимый диапазон.

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

-1
ответ дан 31 October 2019 в 14:14

Другие вопросы по тегам:

Похожие вопросы: