Мне нужно получить разницу в 2 знаковых целых числах. Есть ли функция ABS() на языке ассемблера x86, чтобы я мог это сделать. Любая помощь будет принята с благодарностью.
X86 assembly abs() реализация?
Ответ 1
Если это сборка x86, следующая работа в соответствии с полезной wikipedia. Вычтите одно значение из другого, а затем используйте следующие инструкции для результата:
cdq
xor eax, edx
sub eax, edx
Ответ 2
Так как функция библиотеки 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
БОНУС (из Hacker Delight): Если у вас есть быстрое умножение на +1 и -1, следующий даст вам abs(x):
((x >>> 30) | 1) * x
Ответ 3
Если вы хотите правильно обрабатывать все случаи, вы не можете просто вычесть, а затем принять абсолютное значение. Вы столкнулись с проблемой, потому что различие двух значащих целых чисел не обязательно представляется в виде целого числа со знаком. Например, предположим, что вы используете 32-битные целые числа дополнений, и вы хотите найти разницу между INT_MAX (0x7fffffff) и INT_MIN (0x80000000). Вычитание дает:
0x7fffffff - 0x80000000 = 0xffffffff
который равен -1; когда вы принимаете абсолютное значение, результат получается 1, тогда как фактическая разница между двумя числами 0xffffffff интерпретируется как целое без знака (UINT_MAX).
Разница между двумя целыми знаками всегда представляется как целое число без знака. Чтобы получить это значение (с аппаратным обеспечением с дополнением 2s), вы просто вычитаете меньший вход из большего и интерпретируете результат как целое число без знака; нет необходимости в абсолютном значении.
Здесь один (из многих, а не обязательно лучший) способ сделать это на x86, предполагая, что два целых числа находятся в eax и edx:
cmp eax, edx // compare the two numbers
jge 1f
xchg eax, edx // if eax < edx, swap them so the bigger number is in eax
1: sub eax, edx // subtract to get the difference
Ответ 4
Старая нить, но если я зашел сюда поздно, возможно, тоже... abs - блестящий пример, так что это должно быть здесь.
; abs(eax), with no branches.
; intel syntax (dest, src)
mov ebx, eax ;store eax in ebx
neg eax
cmovl eax, ebx ;if eax is now negative, restore its saved value
Ответ 5
Предполагая, что ваши целые числа находятся в регистрах MMX или XMM, используйте psubd для вычисления разницы, а затем pabsd, чтобы получить абсолютное значение разницы.
Если ваши целые числа находятся в равных, "нормальных" регистрах, затем выполните вычитание, затем трюк cdq, чтобы получить абсолютное значение. Для этого необходимо использовать некоторые конкретные регистры (cdq sign-extends eax в edx, не используя никакой другой регистр), поэтому вы можете делать что-то с другими кодами операций. Например:.
mov r2, r1
sar r2, 31
вычисляет в регистре r2 знак-расширение r1 (0, если r1 положительный или нулевой, 0xFFFFFFFF, если r1 отрицательный). Это работает для всех 32-разрядных регистров r1 и r2 и заменяет инструкцию cdq.
Ответ 6
Короткий, но простой способ, используя условную инструкцию перемещения (имеется в наличии Pentium и выше):
; compute ABS(r1-r2) in eax, overwrites r2
mov eax, r1
sub eax, r2
sub r2, r1
cmovg eax, r2
Подпрограмма устанавливает флаги так же, как и инструкция cmp.
Ответ 7
АБС (EAX)
test eax, eax ; Triger EFLAGS [CF, OF, PF, SF, and ZF]
jns AbsResult ; If (SF) is off, jmp AbsResult
neg eax ; If (SF) is on. (negation nullify by this opcode)
AbsResult:
Если флаги уже установлены с помощью любого генерируемого значения в eax, вам не нужно test. Неправильные предсказания отрасли сделают это медленным, если входные значения будут случайным образом распределены между положительными и отрицательными.
Это работает аналогично для RAX, AX, AL.
Ответ 8
Существует инструкция SUB, если вы хотите сделать A-B. НТН