据我所知,大多数编译器会通过乘法然后向右移位来进行快速除法。例如,如果您检查这个 SO 线程,它会说当您要求 Microsoft 编译器除以 10 时,它会将除数乘以 0x1999999A(即 2^32/10),然后将结果除以 2^32(使用向右移动 32 次)。
(编者注:链接的答案是错误的,直到。编译器不会这样做,因为它并不适用于所有输入。编译器会进行乘法和移位,但使用更复杂的方式来确定魔法常数和移位计数: 为什么 GCC 使用在实现整数除法时乘以一个奇怪的数字?)
到现在为止还挺好。
但是,一旦我在 ARM 上使用 GCC 测试了相同的除以 10,编译器做了一些稍微不同的事情。首先它将被除数乘以 0x66666667 (2^34/10),然后将结果除以 2^34。到目前为止,它与 Microsoft 相同,只是使用了更高的乘数。然而,在那之后,它从结果中减去 (dividend/2^31)。
我的问题:为什么在 ARM 版本上会有额外的减法?你能给我一个数字例子,如果没有那个减法,结果会是错误的吗?
如果你想检查生成的代码,它在下面(带有我的评论):
ldr r2, [r7, #4] @--this loads the dividend from memory into r2
movw r3, #:lower16:1717986919 @--moves the lower 16 bits of the constant
movt r3, #:upper16:1717986919 @--moves the upper 16 bits of the constant
smull r1, r3, r3, r2 @--multiply long, put lower 32 bits in r1, higher 32 in r3
asr r1, r3, #2 @--r3>>2, then store in r1 (effectively >>34, since r3 was higher 32 bits of multiplication)
asr r3, r2, #31 @--dividend>>31, then store in r3
rsb r3, r3, r1 @--r1 - r3, store in r3
str r3, [r7, #0] @--this stores the result in memory (from r3)