bit*_*its 22
这是C库函数abs()在没有分支的情况下在汇编中执行的方式:
abs(x) = (x XOR y) - y
Run Code Online (Sandbox Code Playgroud)
其中y = x >>> 31(假设32位输入),并且>>>是算术右移运算符.
上述公式的解释:
我们只想生成2的负数x.
y = 0xFFFF, if x is negative
0x0000, if x is positive
Run Code Online (Sandbox Code Playgroud)
所以当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);
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
奖金(来自Hacker's Delight):如果您快速乘以+1和-1,以下内容将为您提供abs(x):
((x >>> 30) | 1) * x
Run Code Online (Sandbox Code Playgroud)
Mar*_*ins 17
如果它是x86程序集,那么根据有用的维基百科应该可以使用以下内容.从另一个值中减去一个值,然后在结果上使用这些指令:
cdq
xor eax, edx
sub eax, edx
Run Code Online (Sandbox Code Playgroud)
Ste*_*non 14
如果要正确处理所有情况,则不能只减去然后取绝对值.您将遇到麻烦,因为两个有符号整数的差异不一定表示为有符号整数.例如,假设您使用32位2s补码整数,并且您想要找到INT_MAX(0x7fffffff)和INT_MIN(0x80000000)之间的差异.减法给出:
0x7fffffff - 0x80000000 = 0xffffffff
Run Code Online (Sandbox Code Playgroud)
这是-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
Run Code Online (Sandbox Code Playgroud)
小智 11
老线程,但如果我在这里冲浪很晚你可能也有... 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
Run Code Online (Sandbox Code Playgroud)
假设您的整数在 MMX 或 XMM 寄存器中,用于psubd计算差异,然后pabsd获得差异的绝对值。
如果你的整数在普通的“正常”寄存器中,那么做一个减法,然后是cdq获得绝对值的技巧。这需要使用一些特定的寄存器(cdq符号扩展eax到edx,不使用其他寄存器),所以你可能想要用其他操作码做事。例如:
mov r2, r1
sar r2, 31
Run Code Online (Sandbox Code Playgroud)
在寄存器中计算(如果为正或为零,则为 0,如果为负则为 0xFFFFFFFF )r2的符号扩展。这适用于所有的32位寄存器和并取代指令。r1r1r1r1r2cdq
一种简短而直接的方法,使用条件移动指令(我认为可以使用奔腾及以上版本):
; compute ABS(r1-r2) in eax, overwrites r2
mov eax, r1
sub eax, r2
sub r2, r1
cmovg eax, r2
Run Code Online (Sandbox Code Playgroud)
sub 指令设置的标志与 cmp 指令相同。
| 归档时间: |
|
| 查看次数: |
25424 次 |
| 最近记录: |