Я думаю, что вопрос сам объяснительный, я предполагаю, что он, вероятно, имеет некоторое отношение к переполнению, но тем не менее я действительно не совсем получаю его. Что происходит, поразрядно, под капотом?
Почему делает -(-2147483648) = -2147483648
(по крайней мере, при компиляции в C)?
выражение -(-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). глоток>
Примечание: этот ответ не применяется как таковой на устаревший стандарт 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
, чтобы быть в диапазоне типа; никакой сигнал не повышен.
Это не вопрос 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.
Я собираюсь использовать 4-разрядное число, только делать математику простой, но идея является тем же.
В 4-разрядном числе, возможные значения между 0000 и 1111. Это было бы от 0 до 15, но если Вы хотите представить отрицательные числа, первый бит используется для указания на знак (0 для положительного и 1 для отрицания).
, Таким образом 1111 не 15. Поскольку первый бит равняется 1, это - отрицательное число. Для знания его значения мы используем метод с двумя дополнениями, как уже описано в предыдущих ответах: "инвертируйте биты и добавьте 1":
0001 в двоичном файле 1 в десятичном числе, таким образом 1111-1.
метод с двумя дополнениями идет обоими путями, поэтому при использовании его с каким-либо числом он даст Вам двоичное представление того числа с инвертированным знаком.
Теперь позволяют нам видеть 1000. Первый бит равняется 1, таким образом, это - отрицательное число. Используя метод с двумя дополнениями:
Так 1000-8. Если мы делаем -(-8)
, в двоичном файле это означает -(1000)
, который на самом деле означает использовать метод с двумя дополнениями в 1 000. Как мы видели выше, результат также 1000. Так, в 4-разрядном числе, -(-8)
, равняется-8.
В 32-разрядном числе, -2147483648
в двоичном файле 1000..(31 zeroes)
, но если Вы будете использовать метод с двумя дополнениями, то Вы закончите с тем же значением (результатом является то же число).
Вот почему в 32-разрядном номере -(-2147483648)
, равняется -2147483648
Это зависит от версии C, специфических особенностей реализации и говорим ли мы о значениях литералов или переменных.
первая вещь понять состоит в том, что нет никаких отрицательных целочисленных литералов в C "-2147483648", унарное минус операция, сопровождаемая положительным целочисленным литералом.
Позволяет, предполагают, что мы работаем на типичной 32-разрядной платформе, где интервал и долго составляет и 32 бита, и долго долго 64 бита, и рассмотрите выражение.
(-(-2147483648) ==-2147483648)
компилятор должен найти тип, который может содержать 2147483648, на компиляторе comforming C99 он будет использовать тип "долго долго", но компилятор C90 может использовать тип, "неподписанный длинный".
, Если использование компилятора вводит долго долго затем, ничто не переполняется, и сравнение является ложью. Если компилятор использует неподписанный длинный затем, неподписанные всеобъемлющие правила начинают действовать, и сравнение верно.
По той же причине, что обмотка лентопротяжного механизма противостоит 500 шагам вперед от 000 (до 001 002 003...) покажет 500, и обмотка его назад, 500 шагов назад от 000 (до 999 998 997...) также покажут 500.
Это - дополнительная нотация two. Конечно, с тех пор 2's дополнительная конвенция знака состоит в том, чтобы считать самый верхний бит знаковым битом, результат переполняет представимого диапазона, точно так же, как 2000000000+2000000000 переполнения представимый диапазон.
В результате "водосливный" бит процессора будет установлен (видящий, что это требует доступа к арифметическим флагам машины, обычно не случай на большинстве языков программирования за пределами ассемблера). Это только значение, которое установит "водосливный" бит при отрицании 2's дополнительное число: отрицание любого другого значения находится в диапазоне, представимом 2's дополнение.