指令级的有符号数模数

Signed numbers modulus in instruction level

阅读 前一段时间关于 gccclang 有符号 数模计算的不同实现的有趣讨论提出了一个问题我(讨论中没有讨论)。

为什么要执行这个:

if(num % 2 == 1)

以此开头(clanggcc 相似):

movl    %edi, %eax
shrl    , %eax
addl    %edi, %eax

为什么我们从 ((num >> 31) + num) 开始?为什么取 MSB 并将其添加到号码中?这是哪里来的?

负数% 2的结果是负余数,在C中四舍五入到0。& 1只适用于正数。

因此编译器生成添加符号位数的代码 - 本质上是将负数增加一位,因此 -1 在最后一位有 0,然后 -21 在最后一位,-3 再次有 0... 然后我们屏蔽除最后一位以外的所有内容,然后 从结果中减去符号位 ,例如:

movl    %edi, %edx
shrl    , %edx
leal    (%rdi,%rdx), %eax
andl    , %eax
subl    %edx, %eax

它的性能可能比 idiv 指令稍微好一点。例如,在 Core i7 上,带有 r32 的 IDIV 将具有 17-28 的延迟和 7-12 的吞吐量,而生成的代码中的所有其他代码每个都具有 ~1