据我所知,大多数编译器通过乘法和右移位来进行快速除法。例如,如果您查看这个SO线程,它说当您要求Microsoft编译器执行除以10的操作时,它将通过0x1999999A(即2^32/10)将被除数与乘积相乘,然后将结果除以2^32(使用32次右移)。
(编辑注:直到最近之前,该链接的答案是错误的。编译器不会这样做,因为对于所有输入来说都不是准确的。编译器确实使用乘法和移位,但是有更复杂的方式来确定神奇常数和移位计数:为什么GCC在实现整数除法时使用奇怪的数字进行乘法?)
到目前为止一切顺利。
(编辑注:直到最近之前,该链接的答案是错误的。编译器不会这样做,因为对于所有输入来说都不是准确的。编译器确实使用乘法和移位,但是有更复杂的方式来确定神奇常数和移位计数:为什么GCC在实现整数除法时使用奇怪的数字进行乘法?)
到目前为止一切顺利。
然而,当我在ARM上使用GCC测试同样的除以10时,编译器做了一些略微不同的事情。首先,它将被除数与0x66666667 (2^34/10)相乘,然后将结果除以2^34。到目前为止,这与Microsoft相同,只是使用更高的乘数。之后,它从结果中减去了(被除数/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)
x
的x/10 - 1
结果。(当然,假设使用算术右移。) - Daniel Fischer