Я только что начал читать Восхищение Хакера, и оно определяет брюшной пресс (-231) как-231. Почему это?
Я попробовал printf("%x", abs(0x80000000))
в нескольких различных системах и я возвращаю 0x80000000 на всех них.
Для 32-битного типа данных не существует выражения + 2 ^ 31, потому что наибольшее число равно 2 ^ 31-1 ... подробнее о дополнении до двух ...
{{ 1}}Поскольку целые числа хранятся в памяти как двоичное число с дополнением до двух, положительная версия минимального значения снова становится отрицательной.
То есть (в .NET, но все еще применяется):
int.MaxValue + 1 == int.MinValue // Due to overflow.
И
Math.Abs((long)int.MinValue) == (long)int.MaxValue + 1
На самом деле в C поведение не определено. Из стандарта C99, §7.20.6.1 / 2:
Функции
abs
,labs
иllabs
вычисляют абсолютное значение целого числа] j
. Если результат не может быть представлен, поведение не определено.
и его сноска:
Абсолютное значение самого отрицательного числа не может быть представлено в виде дополнения до двух.
Представление дополнительного числа до двух имеет самый старший бит как отрицательное число. 0x80000000 - это 1, за которой следует 31 ноль, первая 1 представляет -2 ^ 31, а не 2 ^ 31. Следовательно, невозможно представить 2 ^ 31, так как наивысшее положительное число равно 0x7FFFFFFF, то есть 0, за которым следует 31 единица, что равно 2 ^ 31-1.
abs (0x80000000) поэтому не определен в двух дополнениях, так как он слишком велик, из-за этого машина просто сдается и снова выдает 0x80000000. Обычно по крайней мере.
Я думаю, что способ abs
- это сначала проверить знаковый бит
числа. Если его очистить, ничего не делать, поскольку число уже равно + ve
, иначе вернуть 2-дополнение
числа. В вашем случае это число -ve
, и нам нужно найти его 2-дополнение
. Но дополнение до 2 0x80000000
оказывается самим 0x80000000
.
Это возвращается к тому, как хранятся числа.
Отрицательные числа хранятся с помощью двух дополнений. Алгоритм выглядит следующим образом...
Перевернуть все биты, затем добавить 1.
Используя восьмибитные числа для примера...
+0 = -0
00000000 -> 11111111, 11111111 + 1 = 100000000
(но из-за ограничения битов это становится 00000000).
AND...
-128 [aka -(2^7)] равно -(-128)
10000000 -> 01111111, 01111111 + 1 = 10000000
Надеюсь, это поможет.
Очевидно, что математически, |-231| это 231. Если у нас 32 бита для представления целых чисел, мы можем представить не более 232 чисел. Если мы хотим получить представление, симметричное относительно 0, нам нужно принять несколько решений.
Ниже, как и в вашем вопросе, я предполагаю, что ширина чисел 32 бита. Для 0 должен использоваться хотя бы один битовый шаблон. Таким образом, для остальных чисел у нас остается 232-1 или меньше битовых шаблонов. Это число нечетное, поэтому мы можем либо иметь представление, которое не совсем симметрично относительно нуля, либо одно число может быть представлено двумя разными представлениями.
0x80000000
- это "отрицательный ноль" (т.е. ноль), а 0x000000
- "положительный ноль" или обычный ноль. В этой схеме самое положительное число - 0x7fffffff
(2147483647), а самое отрицательное - 0xffffffff
(-2147483647). Преимущество этой схемы в том, что ее легко "декодировать", и в том, что она симметрична. Недостаток этой схемы в том, что вычисление a + b
, когда a
и b
имеют разные знаки, является особым случаем, и с ним нужно разбираться специально. 0x7fffffff
(2147483647), а максимальное отрицательное число 0x80000000
(-2147483647). У 0 снова два представления: положительный ноль - 0x000000
, а отрицательный - 0xffffffff
. Эта схема также имеет проблемы с вычислениями, связанными с отрицательными числами. 1
. В этой схеме существует только один 0, а именно 0x000000
. Самое положительное число - 0x7fffffff
(2147483647), а самое отрицательное - 0x80000000
(-2147483648). В этом представлении присутствует асимметрия. Преимущество этой схемы в том, что не нужно иметь дело с особыми случаями для отрицательных чисел. Представление само позаботится о том, чтобы выдать правильный ответ, если результат не переполняется. По этой причине большинство современных аппаратных средств представляют целые числа в этом представлении. В представлении с двумя дополнениями нет способа представить 231. На самом деле, если вы посмотрите в limits.h
или аналогичный файл вашего компилятора, вы можете увидеть определение для INT_MIN
в таком виде:
#define INT_MIN (-2147483647 - 1)
Это сделано вместо
#define INT_MIN -2147483648
потому что 2147483648 слишком велико, чтобы поместиться в int
в 32-битном представлении с двумя дополнениями. К тому времени, когда унарный оператор минус "получает" число для работы, уже слишком поздно: переполнение уже произошло, и вы не можете его исправить.
Итак, отвечая на ваш первоначальный вопрос, абсолютное значение самого отрицательного числа в представлении с двумя дополнениями не может быть представлено в этой кодировке. Кроме того, из вышесказанного следует, что для перехода от отрицательного значения к положительному в представлении с двумя дополнениями нужно взять его дополнение к единицам, а затем добавить 1. Таким образом, для 0x80000000
:
1000 0000 0000 0000 0000 0000 0000 0000 original number
0111 1111 1111 1111 1111 1111 1111 1111 ones' complement
1000 0000 0000 0000 0000 0000 0000 0000 + 1
вы получите исходное число.
0x8000 .. хранится как 10000 .... (двоичный). Это известно как дополнение до двух, что означает, что старший бит (тот, что слева) используется для хранения знака значения, а отрицательные значения сохраняются с отрицательным двоичным кодом - 1. Функция abs () теперь проверяет знаковый бит, видит, что он установлен, и вычисляет положительное значение.
Теперь это снова отрицательное число, которое нам не нужно, причина в переполнении, попробуйте число 0x9000 ... которое равно 10010. ..
С этим числом переполнение останавливается бит 0 справа
, потому что для выполнения этой операции используется команда neg.
В книге «Искусство программирования на ассемблере» они сказали вот что.
Если операнд равен нулю, его знак не меняется, хотя при этом снимается флаг переноса . Отрицание любого другого значения устанавливает флаг переноса. Отрицание байта , содержащего -128, слова, содержащего -32 768, или двойного слова, содержащего -2 147 483 648, не изменяет операнд, но устанавливает флаг переполнения . Neg всегда обновляет флаги A, S, P, и Z, как если бы вы использовали подинструкцию
источник: http://www.arl.wustl.edu /~lockwood/class/cs306/books/artofasm/Chapter_6/CH06-2.html#HEADING2-313 Таким образом, он установит флаг переполнения и будет молчать. Вот почему.