Подтвердить что ты не робот

X86 assembly abs() реализация?

Мне нужно получить разницу в 2 знаковых целых числах. Есть ли функция ABS() на языке ассемблера x86, чтобы я мог это сделать. Любая помощь будет принята с благодарностью.

4b9b3361

Ответ 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. НТН