据我所知,大多数编译器会通过乘法然后向右移位进行快速除法。例如,如果你检查this SO thread它说当你要求Microsoft编译器除以10时它会将被除数乘以0x1999999A(即2 ^ 32/10),然后将结果除以2 ^ 32 (使用32向右移动)。
到目前为止一切顺利。
然而,一旦我使用GCC在ARM上测试相同的除法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)
答案 0 :(得分:9)
然而,在此之后,它从结果中减去(被除数/ 2 ^ 31)。
实际上,它减去dividend >> 31
,其中-1
为负dividend
,0为非负红利,当右移负整数为算术右移(和{ {1}}是32位宽。)
int
对于0x6666667 = (2^34 + 6)/10
,我们已经x < 0
撰写了x = 10*k + r
,
-10 < r <= 0
现在,负整数的算术右移产生0x66666667 * (10*k+r) = (2^34+6)*k + (2^34 + 6)*r/10 = 2^34*k + 6*k + (2^34+6)*r/10
的底限,所以
v / 2^n
结果
(0x66666667 * x) >> 34
所以我们需要看到
k + floor((6*k + (2^34+6)*r/10) / 2^34)
正确的不平等很容易,-2^34 < 6*k + (2^34+6)*r/10 < 0
和k
都是非正数,而且两者都不是0.
对于左不等式,需要进行更多分析。
r
因此r >= -9
的绝对值最多为(2^34+6)*r/10
。
2^34+6 - (2^34+6)/10
所以|k| <= 2^31/10,
。
还有待验证
|6*k| <= 3*2^31/5
是的,是的。
答案 1 :(得分:5)
x SAR 31
的{{1}}(-1)为负值0xffffffff
,x
为正值。
因此,如果被除数是负数,0x00000000
从结果中减去-1(与加1相同)。
假设您的股息为rsb
。只需乘法和移位就可以获得结果-60
,因此它会减去-1以获得-7
的预期结果。