Assembly x86汇编abs()的实现?
我需要得到2个有符号整数的差。x86汇编语言中是否有ABS()函数,我可以这样做。任何帮助都将不胜感激。如果是x86程序集,以下内容应该可以使用。从另一个值中减去一个值,然后对结果使用以下说明: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
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: