假设我们有两个寄存器长度的有符号整数,例如a和b。 我们想要计算值(a + b) / 2,无论是向上舍入、向下舍入、朝零舍入还是远离零,以任何一种方式都可以(即我们不关心舍入方向)。
结果是另一个寄存器长度的有符号整数(很明显,平均值必须在一个寄存器长度的有符号整数范围内)。
如何最快地执行此计算?
您可以选择最初两个整数将位于哪些寄存器中,以及平均值将最终位于哪个寄存器中。
注1:对于无符号整数,我们可以用两条指令完成。 这可能是最快的方法,尽管在Intel CPU上,通过旋转需要超过1个uop。 但是当计数只有1时,只需要几个。 在有关无符号平均数的Q&A上An answer讨论了效率。
两个数字分别在
结果是另一个寄存器长度的有符号整数(很明显,平均值必须在一个寄存器长度的有符号整数范围内)。
如何最快地执行此计算?
您可以选择最初两个整数将位于哪些寄存器中,以及平均值将最终位于哪个寄存器中。
注1:对于无符号整数,我们可以用两条指令完成。 这可能是最快的方法,尽管在Intel CPU上,通过旋转需要超过1个uop。 但是当计数只有1时,只需要几个。 在有关无符号平均数的Q&A上An answer讨论了效率。
add rdi, rsi
rcr rdi, 1
两个数字分别在
rdi
和rsi
寄存器中,平均数存储在rdi
寄存器中。但对于带符号的数字,-1 + 3
会设置CF标志,并将1
旋转到符号位,导致未给出正确答案+1
。
注2:我指定了寄存器长度的有符号整数,因此我们不能简单地使用movsxd
或cdqe
指令来扩展整数。
我接近解决方案的方法需要使用四条指令,其中一条是rcr
指令,在英特尔处理器上需要3个微操作,在AMD Zen上只需要1个(https://uops.info/):
add rdi, rsi
setge al
sub al, 1 # CF = !(ge) = !(SF==OF)
rcr rdi, 1 # shift CF into the top of (a+b)>>1
我认为一个更简短的解决方案可能在于以某种方式结合中间两个指令,即执行CF ← SF ≠ OF
。
我看过这个问题,但那并非x86特定,并且没有一个答案似乎像我的方案一样好。
rdi
= -1和rsi
= 3开始尝试。执行add rdi, rsi
指令将设置CF标志位,并通过rcr rdi, 1
指令旋转到rdi
的符号位,导致结果为负数。但正确答案是1。 - Bernardadd
更改为adc
会使其稍微好一些,但存在某种不一致的舍入。 - Bernardsetge rax
这样的东西不存在;你必须使用 8 位寄存器。 - Nate Eldredge