【发布时间】:2013-04-19 13:16:11
【问题描述】:
据我所知,大多数编译器会通过乘法然后向右位移来进行快速除法。例如,如果您检查this SO thread,它表示当您要求 Microsoft 编译器除以 10 时,它会将除数乘以 0x1999999A(即 2^32/10),然后将结果除以 2^32(使用向右移动 32 次)。
(编者注:链接的答案在此之前是错误的。编译器不会这样做,因为它并不适用于所有输入。编译器会进行乘法和移位,但使用更复杂的方式来确定魔法常数和移位计数:@ 987654322@)
到目前为止一切顺利。
不过,一旦我在 ARM 上使用 GCC 测试了相同的除以 10,编译器做了一些稍微不同的事情。首先它将被除数乘以 0x66666667 (2^34/10),然后将结果除以 2^34。到目前为止,它与 Microsoft 相同,只是使用了更高的乘数。然而,在那之后,它从结果中减去 (dividend/2^31)。
我的问题:为什么在 ARM 版本上会有额外的减法?你能给我一个数字例子,如果没有那个减法,结果会是错误的吗?
如果你想检查生成的代码,它在下面(使用我的 cmets):
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/10 - 1负x。 (当然,假设算术右移。) -
我可以看到,如果我使用乘法/移位方法得到 -99 / 10,我将得到 -10。但是如果我从中减去 1,我会得到 -11,而我想要的是 -9 不是吗?
-
你减去
-1,即你加1。
标签: gcc assembly arm integer-division