【问题标题】:Fast Division on GCC/ARMGCC/ARM 上的快速除法
【发布时间】: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 - 1x。 (当然,假设算术右移。)
  • 我可以看到,如果我使用乘法/移位方法得到 -99 / 10,我将得到 -10。但是如果我从中减去 1,我会得到 -11,而我想要的是 -9 不是吗?
  • 你减去-1,即你加1。

标签: gcc assembly arm integer-division


【解决方案1】:

x SAR 310xffffffff (-1),表示 x 的负值,0x00000000 表示正值。

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

假设您的股息是-60。通过乘法和移位,您将得到结果-7,因此它减去-1 以获得-6 的预期结果。

【讨论】:

    【解决方案2】:

    然后,它从结果中减去 (dividend/2^31)。

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

    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
    

    正确的不等式很简单,kr 都是非正数,而且不是都是 0。

    对于左不等式,需要进一步分析。

    r >= -9
    

    所以(2^34+6)*r/10的绝对值最多为2^34+6 - (2^34+6)/10

    |k| <= 2^31/10,
    

    所以|6*k| &lt;= 3*2^31/5.

    还有待验证

    6 + 3*2^31/5 < (2^34+6)/10
    1288490194   < 1717986919
    

    是的,没错。

    【讨论】:

    • 感谢您的详细说明。
    猜你喜欢
    • 2012-06-25
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多