C如何在内部执行%操作

时间:2013-06-26 18:25:05

标签: c modulo

我很想理解mod操作背后的逻辑,因为我知道可以执行位移操作来执行不同的操作,例如位移到乘法。

我可以看到它完成的一种方法是通过递归算法继续划分,直到你不能再分裂,但这看起来效率不高。

任何想法都会有所帮助。提前谢谢!

4 个答案:

答案 0 :(得分:14)

快速版本是:取决于硬件,优化器,if it's division by a constant or not(pdf),是否有要检查的例外(例如模数为0),是否以及如何处理负数({{3} })等...

对于无符号整数,R给出了一个很好的,简洁的答案,但除非你精通C语言,否则很难理解。

R所照亮的技术的关键是去除q的倍数,直到不再有q的倍数。我们可以通过一个简单的循环天真地做到这一点:

while (p >= q) p -= q; // One liner, woohoo!

代码可能很短,但是对于较大的p值和较小的q值,这可能需要很长时间。

一次剥离一个q比一次​​删除多个q更好。请注意,我们实际上想要删除尽可能多的q - 即floor(p/q)q个......实际上,这是一种有效的技术。对于无符号整数,可以预期p % q == p - (p / q) * q。 (注意无符号整数除法向下舍入。)

但这几乎就像作弊,因为分裂和剩余操作是如此密切相关。 (事实上​​,如果硬件本身支持除法,它通常支持除法和计算余数运算,因为它们之间的关系非常紧密。)

假设我们无法访问除法,我们如何找到大于1的q倍数才能删除?在硬件中,固定的移位操作是便宜的(如果不是实际上是免费的)并且在概念上表示乘以2的非负功率。例如,将位串向左移位3相当于乘以8(即2 ^ 3),例如2。 5十进制相当于'101'二进制。通过在右侧添加三个零(给出'101000')将结果'101'变为二进制,结果为十进制50 - 五次八。

同样,班次操作作为软件操作非常便宜,而且你很难找到一个不能快速支持它们的控制器。 (像ARM这样的架构甚至可以将移位与其他指令结合起来,使它们在很多时候都是“免费的”。)

对于这些转变操作,ARMed(无法抗拒),我们可以按照以下步骤进行:

  1. 找出最大的2的幂,我们可以乘以q并且仍然小于p
  2. 从2的最大幂到最小的幂,乘以q乘以2的幂,如果它小于p的左边,则从p的左边减去它。
  3. 无论你剩下什么,剩下的就是。
  4. 为什么这样做?因为最后你会发现两个减去的所有权力实际上总和为floor(p / q)!不要相信我,this is a scary question for C++知道类似的知识。

    打破R的答案:

    #define HI (-1U-(-1U/2))
    

    这实际上为您提供了一个无符号整数,只设置了最高位。

    unsigned i;
    for (i=0; !(HI & (q<<i)); i++);
    

    该行实际上发现两个q的最高功率可以在溢出无符号整数之前相乘。这不是绝对必要的,但除了增加所需的执行时间外,它不会改变结果。

    如果您不熟悉此行中的C-isms:

    1. (q<<i)左移位i。回想一下,相当于乘以2 ^ i
    2. HI & (q<<i)执行按位AND。由于HI仅填充其最高位,因此当(q<<i)足够大以使顶部位非零时,这将仅导致非零值。还有一个向左移动,并且会出现整数溢出。
    3. !(HI & (q<<i))为零时,
    4. (HI & (q<<i))为'true',否则为'false'。
    5. do { if (p >= (q<<i)) p -= (q<<i); } while (i--);

      这是一个简单的递减循环do { .... } while (i--);。请注意,在i上使用后递减,因此循环执行,然后检查i是否不为零,然后从i中减去一个,然后再检查它导致true继续。这具有循环在i为0时最后执行的属性。这很重要,因为我们可能需要删除q的未经过复制的副本。

      if (p >= (q<<i))检查2 ^ i * q是否小于或等于p。如果是,p -= (q<<i)将其剥离。

      剩下的就剩下了。

答案 1 :(得分:5)

虽然大多数C实现在具有除法指令的硬件上运行,但余数操作可以大致像这样执行,用于计算p%q,假设无符号值:

#define HI (-1U-(-1U/2))
unsigned i;
for (i=0; !(HI & (q<<i)); i++);
do { if (p >= (q<<i)) p -= (q<<i); } while (i--);

结果余数在p

答案 2 :(得分:2)

除了使用移位的硬件指令和实现,R.. suggests,还有reciprocal multiplication

%的右侧是常量,在编译时已知,可以使用此技术。
倒数乘法用于实现除法,但根据公式%将其用于a%b == a-(a/b)*b很容易。

答案 3 :(得分:0)

根据优化器的智能,模数库2有一个快捷方式。例如,a % 32可以实现为a & 31。一般来说,a % (2^N) == a & (2^N -1)。与分裂相比,这是闪电般快速的。大多数分频器(通常是硬件)需要至少1个周期来计算结果的每个位,而逻辑AND只是几个周期操作(在流水线中)。

编辑:仅当a未签名时才有效!