Assembly x86汇编abs()的实现?

Assembly x86汇编abs()的实现?,assembly,x86,Assembly,X86,我需要得到2个有符号整数的差。x86汇编语言中是否有ABS()函数,我可以这样做。任何帮助都将不胜感激。如果是x86程序集,以下内容应该可以使用。从另一个值中减去一个值,然后对结果使用以下说明: cdq xor eax, edx sub eax, edx 如果你想正确处理所有的情况,你不能只是减去然后取绝对值。您将遇到麻烦,因为两个有符号整数的差不一定可以表示为有符号整数。例如,假设您使用的是32位2s补码整数,并且希望找到INT\u MAX(0x7fffffff)和INT\u MIN(0x8

我需要得到2个有符号整数的差。x86汇编语言中是否有ABS()函数,我可以这样做。任何帮助都将不胜感激。

如果是x86程序集,以下内容应该可以使用。从另一个值中减去一个值,然后对结果使用以下说明:

cdq
xor eax, edx
sub eax, edx

如果你想正确处理所有的情况,你不能只是减去然后取绝对值。您将遇到麻烦,因为两个有符号整数的差不一定可以表示为有符号整数。例如,假设您使用的是32位2s补码整数,并且希望找到
INT\u MAX
0x7fffffff
)和
INT\u MIN
0x8000000
)之间的差异。减去得出:

0x7fffffff - 0x80000000 = 0xffffffff
这是
-1
;当取绝对值时,得到的结果是
1
,而两个数字之间的实际差值被解释为无符号整数(
UINT\u MAX

两个有符号整数之间的差始终可以表示为无符号整数。要获得该值(使用2s补码硬件),只需从较大的输入中减去较小的输入,并将结果解释为无符号整数;不需要绝对值

假设这两个整数位于
eax
edx
中,那么在x86上有一种方法(很多方法中的一种,但不一定是最好的)可以实现这一点:

    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
cmp-eax,edx//比较这两个数字
jge 1f
xchg-eax,edx//如果eax
如果要执行A-B操作,则有子指令。
HTH

假设整数在MMX或XMM寄存器中,使用
psubd
计算差值,然后使用
pabsd
获得差值的绝对值

如果整数在普通的“正常”寄存器中,则进行减法运算,然后使用
cdq
技巧获得绝对值。这需要使用一些特定寄存器(
cdq
sign将
eax
扩展到
edx
,不使用其他寄存器),因此您可能需要使用其他操作码。例如:

mov  r2, r1
sar  r2, 31

在寄存器
r2
中计算
r1
的符号扩展名(如果
r1
为正,则为0;如果
r1
为负,则为0xFFFFFFFF)。这适用于所有32位寄存器
r1
r2
,并取代
cdq
指令。

使用条件移动指令(我认为Pentium和更高版本可用),简单明了:


sub指令将标志设置为与cmp指令相同。

旧线程,但如果我在这里浏览得太晚,您可能会。。。 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

C库函数
abs()
在汇编中执行此操作时没有分支:

   abs(x) = (x XOR y) - y
其中
y=x>>>31
(假设32位输入),并且
>>
是算术右移运算符

对上述公式的解释: 我们只想生成负
x
的2的补码

y = 0xFFFF, if x is negative
    0x0000, if x is positive
所以当
x
为正时
XOR 0x0000
等于
x
。当
x
为负时,
xxor 0xFFFF
等于
x
的1的补码。现在我们只需要添加
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):

ABS(EAX)

如果标志已经由eax中生成的值设置,则不需要进行
测试
。如果输入值随机分布在正和负之间,分支预测失误将使速度变慢


这与RAX、AX、AL的工作方式相同。

您可以比较并有条件地交换,然后减去。您在哪个平台上?没有“汇编语言”,只有“x86汇编”或“ARM汇编”等。我如何比较和有条件地交换,然后减去。你能在x86中提供一个例子吗?你指的是距离,而不是差异。
或reg,reg
总是比
测试reg,reg
更糟糕的选择。此外,分支不是“一个时钟”。它们要么是~0个时钟(预测正确),要么是~15个时钟(预测失误)。cmov是P6(ppro/PII)的新版本,但现在你可以假设它。gcc会。使用
jge
可能会导致cpu中的
分支预测器
出现
错误预测
,从而显著降低cpu速度。因此,如果性能受到关注,最好使用@bits或@hal的答案。通过避免分支预测器,这是非常简单和有效的,肯定应该被接受为答案。
  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
      ((x >>> 30) | 1) * x
  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: