Я только начал читать Hacker Delight и он определяет abs (-2 31) как -2 31. Почему это?
Я попробовал printf("%x", abs(0x80000000))
в нескольких разных системах, и я возвращаюсь на все 0x80000000.
Я только начал читать Hacker Delight и он определяет abs (-2 31) как -2 31. Почему это?
Я попробовал printf("%x", abs(0x80000000))
в нескольких разных системах, и я возвращаюсь на все 0x80000000.
Для 32-битного типа данных нет выражения + 2 ^ 31, потому что наибольшее число составляет 2 ^ 31-1... подробнее о two дополнение...
Собственно, в C поведение undefined. Из стандарта C99, §7.20.6.1/2:
Функции
abs
,labs
иllabs
вычисляют абсолютное значение целого числаj
. Если результат не может быть представлен, поведение undefined.
и его сноска:
Абсолютное значение самого отрицательного числа не может быть представлено в двойном дополнении.
Поскольку целые числа хранятся в памяти в виде двоичного числа с двумя дополнениями, положительная версия минимального значения переполняется обратно на отрицательный.
То есть (в .NET, но все же применяется):
int.MaxValue + 1 == int.MinValue // Due to overflow.
и
Math.Abs((long)int.MinValue) == (long)int.MaxValue + 1
Очевидно, математически, | & minus; 2 31 | 2 31. Если у нас есть 32 бита для представления целых чисел, мы можем представить не более 2 32 чисел. Если мы хотим, чтобы представление было симметричным относительно 0, мы должны принять несколько решений.
Для следующего, как и в вашем вопросе, я предполагаю 32-битные номера. По крайней мере, один бит-шаблон должен использоваться для 0. Таким образом, мы оставим нам 2 32 & minus; 1 или менее битовые шаблоны для остальных чисел. Это число нечетно, поэтому мы можем либо иметь представление, которое не является точно симметричным относительно нуля, либо иметь одно число с двумя различными представлениями.
0x80000000
- "отрицательный ноль" (т.е. Нуль), а 0x00000000
- "положительный ноль" или обычный ноль. В этой схеме наиболее положительное число 0x7fffffff
(2147483647), а наибольшее отрицательное число - 0xffffffff
(& plusmn; 2147483647). Преимущество этой схемы состоит в том, что нам легко "декодировать" и что она симметрична. Эта схема имеет недостаток в том, что вычисление a + b
, когда a
и b
имеют разные знаки, является особым случаем и должно быть рассмотрено специально.0x7fffffff
(2147483647), а максимальное отрицательное число 0x80000000
(& plusmn; 2147483647). Повторяются два представления 0: положительный нуль равен 0x00000000
, а отрицательный - 0xffffffff
. Эта схема также имеет проблемы с вычислениями с отрицательными числами.1
. В этой схеме есть только одно 0, а именно 0x00000000
. Наиболее положительное число 0x7fffffff
(2147483647), а наибольшее отрицательное число 0x80000000
(& minus; 2147483648). В этом представлении имеется асимметрия. Преимущество этой схемы состоит в том, что не нужно иметь дело со специальными случаями для отрицательного числа. Представление заботится о том, чтобы дать вам правильный ответ, пока результат не переполняется. По этой причине большая часть текущего аппаратного обеспечения представляет собой целые числа в этом представлении.В двух дополнительных представлениях нет возможности представить 2 31. Фактически, если вы посмотрите на свой компилятор 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
вы получите исходный номер.
Это относится к тому, как сохраняются числа.
Отрицательные числа сохраняются с использованием двух дополнений. Алгоритм выглядит как...
Переверните все биты, затем добавьте 1.
Использование восьмибитовых чисел для примеров...
+0 = -0
00000000 → 11111111, 11111111 + 1 = 100000000
(но из-за ограничения бит это становится 00000000).
и...
-128 [aka - (2 ^ 7)] равно - (- 128)
10000000 → 01111111, 01111111 + 1 = 10000000
Надеюсь, что это поможет.
Представление двух номеров дополнений имеет самый старший бит как отрицательное число. 0x80000000 - 1, затем 31 нуль, первый 1 представляет -2 ^ 31 не 2 ^ 31. Поэтому невозможно представить 2 ^ 31, так как наивысшее положительное число равно 0x7FFFFFFF, а 0 равно 31, что равно 2 ^ 31-1.
abs (0x80000000) поэтому undefined в двух дополнениях, поскольку он слишком велик, из-за этого машина просто сдается и снова дает вам 0x80000000. Как правило, по крайней мере.
Я думаю, что способ abs
заключается в том, чтобы сначала проверить sign bit
числа. Если его ясность ничего не делает, поскольку число уже +ve
else возвращает 2 complement
числа. В вашем случае число -ve
, и нам нужно найти его 2 complement
. Но 2 дополнение к 0x80000000
оказывается 0x80000000
.
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 Поэтому он установит флаг переполнения и будет молча. Это причина.