GCC/ARM上的快速划分

GCC/ARM上的快速划分,gcc,assembly,arm,integer-division,Gcc,Assembly,Arm,Integer Division,据我所知,大多数编译器都会通过乘法然后向右移位来进行快速除法。例如,如果您检查它,它表示当您要求Microsoft编译器进行除法10时,它将把被除数乘以0x1999999A(即2^32/10),然后将结果除以2^32(使用32个向右移位) (编者按:这个链接的答案在之前是错误的。编译器不会这样做,因为它不是对所有输入都精确的。编译器会进行乘法和移位,但使用更复杂的方法来确定神奇常数和移位计数:) 到目前为止还不错 不过,有一次我在ARM上使用GCC测试了相同的10除法,编译器做了一些稍微不同的

据我所知,大多数编译器都会通过乘法然后向右移位来进行快速除法。例如,如果您检查它,它表示当您要求Microsoft编译器进行除法10时,它将把被除数乘以0x1999999A(即2^32/10),然后将结果除以2^32(使用32个向右移位)

(编者按:这个链接的答案在之前是错误的。编译器不会这样做,因为它不是对所有输入都精确的。编译器会进行乘法和移位,但使用更复杂的方法来确定神奇常数和移位计数:)


到目前为止还不错

不过,有一次我在ARM上使用GCC测试了相同的10除法,编译器做了一些稍微不同的事情。首先将股息乘以0x666667(2^34/10),然后将结果除以2^34。到目前为止,它和微软一样,只是使用了更高的乘数。然而,在那之后,它从结果中减去(股息/2^31)

我的问题:为什么ARM版本上有额外的减法?你能给我一个数字例子,如果没有减法,结果会是错误的吗

如果您想检查生成的代码,请参见下面的(带我的注释):


x SAR 31
对于
x
的负值为
0xffffff
(-1),对于正值为
0x00000000

因此,如果被除数为负数,
rsb
从结果中减去-1(与加1相同)

假设您的红利是
-60
。只需使用乘法和移位,就可以得到结果
-7
,因此它减去-1就可以得到预期的结果
-6

然而,在那之后,它从结果中减去(股息/2^31)

实际上,当右移负整数为算术右移(且
int
为32位宽)时,它减去
被除数>>31
,即
-1
表示负
被除数
,0表示非负被除数


所以对于
x<0
,我们用
x=10*k+r
x=10*k+r
,这是负值,整数除法截断,对于负值
x
,乘法和移位产生
x/10-1
。(当然,假设算术右移。)我可以看到,如果我用乘法/移位法得到-99/10,结果会得到-10。但是如果我从中减去1,我会得到-11,而我想要的是-9,不是吗?你减去
-1
,也就是说,你加1。谢谢你的详细说明。
        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) 
0x6666667 = (2^34 + 6)/10
0x66666667 * (10*k+r) = (2^34+6)*k + (2^34 + 6)*r/10 = 2^34*k + 6*k + (2^34+6)*r/10
(0x66666667 * x) >> 34
k + floor((6*k + (2^34+6)*r/10) / 2^34)
-2^34 < 6*k + (2^34+6)*r/10 < 0
r >= -9
|k| <= 2^31/10,
6 + 3*2^31/5 < (2^34+6)/10
1288490194   < 1717986919