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
; 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
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
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:
9条答案
按热度按时间cxfofazt1#
这就是C库函数
abs()
在汇编中不分支的方式:其中
y = x >> 31
(假设输入为32位),>>
是算术右移运算符。**上述公式的解释:**我们只想生成2的负数
x
的补数。因此,当
x
为正时,x XOR 0x00000000
等于x
。当x
为负时,x XOR 0xFFFFFFFF
等于x
的1的补码。现在我们只需要将1
相加,得到它的2的补码,这就是表达式-y
所做的。因为0xFFFFFFFF
在十进制中是-1。让我们看看
gcc
(我的机器上是4.6.3)为以下代码生成的汇编:C代码:
gcc 4.6.3生成的汇编代码段(AT&T语法),附带我的注解:
**奖金(来自Hacker's Delight):**如果你有一个快速乘以+1和-1,以下将为你提供32位
x
的abs(x)
:xienkqul2#
老线索,但如果我在这里冲浪晚了,你可能也有... abs是一个很好的例子,所以这应该在这里。
eaf3rand3#
如果它是x86汇编,根据曾经有用的维基百科,下面应该可以工作。从另一个值中减去一个值,然后对结果使用这些指令:
sc4hvdpw4#
如果你想正确处理所有的情况,你不能只做减法,然后取绝对值。你会遇到麻烦,因为两个有符号整数的差不一定能表示为一个有符号整数。例如,假设你使用的是32位2s补码整数,你想找出
INT_MAX
(0x7fffffff
)和INT_MIN
(0x80000000
)之间的差。相减得到:即
-1
;当你取绝对值时,你得到的结果是1
,而这两个数字之间的实际差值是0xffffffff
,被解释为一个无符号整数(UINT_MAX
)。两个有符号整数之间的差 * 总是可以表示为无符号整数。要得到这个值(使用2s补码硬件),只需从较大的输入中减去较小的输入,并将结果解释为无符号整数;不需要绝对值。
下面是在x86上执行此操作的一种(多种方法中的一种,但不一定是最好的)方法,假设两个整数在
eax
和edx
中:8qgya5xd5#
假设整数在MMX或XMM寄存器中,使用
psubd
计算差值,然后使用pabsd
获得差值的绝对值。如果你的整数在普通的“普通”寄存器中,那么做减法,然后
cdq
技巧来获得绝对值。这需要使用一些特定的寄存器(cdq
符号扩展eax
到edx
,不使用其他寄存器),所以你可能想用其他操作码来做事情。例如:在寄存器
r2
中计算r1
的符号扩展(如果r1
为正或零,则为0,如果r1
为负,则为0xFFFFFFFF)。这适用于所有32位寄存器r1
和r2
,并取代cdq
指令。gmxoilav6#
一个简短但直接的方法,使用条件移动指令(我认为奔腾和更高版本可用):
sub指令设置的标志与cmp指令相同。
8aqjt8rx7#
ABS(EAX)
如果在eax中生成值的东西已经设置了标志,那么就不需要
test
了。如果输入值随机分布在正负之间,分支预测错误会使这个过程变慢。这对RAX、AX、AL的工作方式相同。
r1zk6ea18#
一次8个字符
ddrv8njm9#
如果您要执行A-B. HTH,则有SUB指令