Вот как функция библиотеки C abs()
делает это в сборке без ветвления:
abs(x) = (x XOR y) - y
, где y = x >>> 31
(при условии 32-битного ввода), а >>>
- арифметический оператор смещения вправо.
Пояснение к вышеприведенной формуле:
Мы хотим сгенерировать только 2 с отрицательным x
.
y = 0xFFFF, if x is negative
0x0000, if x is positive
То есть, когда x
положительно, x XOR 0x0000
равно x
. И когда x
отрицательно, x XOR 0xFFFF
равно 1 дополнению x
. Теперь нам просто нужно добавить 1
, чтобы получить дополнение к его 2, что и делает выражение -y
. Потому что 0xFFFF
это -1 в десятичном виде.
Давайте посмотрим на сборку, сгенерированную для следующего кода gcc
(4.6.3 на моем компьютере):
C код:
main()
{
int x;
int output = abs(x);
}
gcc 4.6.3 сгенерированный фрагмент сборки (синтаксис AT & T), с моими комментариями:
movl -8(%rbp), %eax # -8(%rbp) is memory for x on stack
sarl $31, %eax # shift arithmetic right: x >>> 31, eax now represents y
movl %eax, %edx #
xorl -8(%rbp), %edx # %edx = x XOR y
movl %edx, -4(%rbp) # -4(%rbp) is memory for output on stack
subl %eax, -4(%rbp) # (x XOR y) - y
БОНУС (от Восторг хакера ): Если вы быстро умножите на +1 и -1, следующее даст вам abs(x)
:
((x >>> 30) | 1) * x