我需要得到2个有符号整数的区别。是否有x86汇编语言的ABS()函数,所以我可以这样做。任何帮助将不胜感激。
答案 0 :(得分:20)
这是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
等于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
BONUS(来自Hacker's Delight):如果您快速乘以+1和-1,以下内容将为您提供abs(x)
:
((x >>> 30) | 1) * x
答案 1 :(得分:17)
如果是x86程序集,则以下according to the ever useful wikipedia应该有效。从另一个值中减去一个值,然后在结果上使用这些指令:
cdq
xor eax, edx
sub eax, edx
答案 2 :(得分:14)
如果要正确处理所有情况,则不能只减去然后取绝对值。您将遇到麻烦,因为两个有符号整数的差异不一定表示为有符号整数。例如,假设您使用的是32位2s补码整数,并且您希望找到INT_MAX
(0x7fffffff
)和INT_MIN
(0x80000000
)之间的差异。减法给出:
0x7fffffff - 0x80000000 = 0xffffffff
是-1
;当你取绝对值时,得到的结果是1
,而两个数字之间的实际差异是0xffffffff
被解释为无符号整数(UINT_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
答案 3 :(得分:8)
旧线程,但如果我在这里冲浪很晚你可能也有... 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
答案 4 :(得分:4)
假设你的整数在MMX或XMM寄存器中,使用psubd
计算差值,然后pabsd
得到差值的绝对值。
如果你的整数在普通的“普通”寄存器中,那么进行减法,然后用cdq
技巧获得绝对值。这需要使用一些特定的寄存器(cdq
符号扩展eax
到edx
,不使用其他寄存器),因此您可能希望使用其他操作码。 E.g:
mov r2, r1
sar r2, 31
在寄存器r2
中计算r1
的符号扩展名(如果r1
为正或0,则为0;如果r1
为负,则为0xFFFFFFFF)。这适用于所有32位寄存器r1
和r2
,并替换cdq
指令。
答案 5 :(得分:4)
一种简短但直截了当的方式,使用条件移动指令(我认为Pentium可用):
; compute ABS(r1-r2) in eax, overwrites r2
mov eax, r1
sub eax, r2
sub r2, r1
cmovg eax, r2
子指令将标志设置为与cmp指令相同。
答案 6 :(得分:1)
ABS(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,它的工作方式相同。
答案 7 :(得分:0)
如果您想要的是A-B,则有SUB指令。 HTH