这是编译器如何通过使用逐位操作来划分的?

时间:2014-09-11 08:05:14

标签: c algorithm bit-manipulation

使用按位运算符划分

我在stackoverflow上发现了一篇帖子 看到这个奇怪的方法除以3(What's the fastest way to divide an integer by 3?) 它说: n / 3 =(n * 0x55555556)>> 32 即可。这将乘以 0x55555556 并取最重要的32位。对于n为32位的数字,这是正确的。
当我开始挖掘它背后的逻辑时,我意识到这可以推广。例如,如果我们认为n< 128;即,7位宽,然后: n / 3 =(n * 0x56)>> 8 即可。 那么这里发生了什么?好吧, 0x56 = 86 =(258/3) 258 是第一个大于256的数字,可以被3整除。现在,
2n = 258n - 256n
= => n = 129n - 128n 对于任何n;
但是如果限制n< 128;然后129n - 128n与(129n / 128)的商相同 ==> n = 129n / 128 ; n< 128个
==> n / 3 = 43n / 128 ;对于n< 128个
==> n / 3 = 86n / 256 ; n< 128个
==> n / 3 =(n * 0x56)>> 8 ;

令人惊讶的是,这可以推广到其他部门。例如,考虑除以5:
4n = 260n - 256n ==>所有n的 n = 65n - 64n 如果 n< 64; n = 65n / 64
==> n / 5 = 13n / 64 ; n< 64个
==> n / 5 =(13 * n)>> 6 ;
类似地, n / 5 =(52 * n)>> 8 ; n< 64个

我们可以继续沿着同样的路线前进: n / 5 = 205n / 1024 =(205 * n)>> 10 对于n< 1024

因此;我试图找到一条通用规则,为任何 a 找到不适用? 首先找到两个幂(比如 2 ^ m ),它是a的倍数的一个(如果a = 5则为1024);
2 ^ m + 1 = k a ;对于一些k
所以工作就是找出 k ,同时保持 m 固定(比如32): 2 ^ m + 1 = k
一个即可。可以有多种解决方案;一旦 m 被修复,将只有一个。

然后; n / a = k * n>>米;对于所有n< 2 ^ M

现在这是编译器的工作方式吗?在其中一条评论中使用 kol 提供的formula generator
保持m = 32和n = 12;公式生成器给出:
a = 3: n / 3 =(1431655766 * n)>> 32
a = 5: n / 5 =(858993460 * n)>> 32
a = 7: n / 7 =(613566757 * n)>> 32

然而,当我看到我的装配输出时,我分别得到 1431655766 1717986919 -1840700269 除以3,5和7。
此外,如果我将数据类型更改为unsigned int,那么对于3,5和7,我分别得到 -1431655765 -858993459 613566757

<小时/> 正如你所看到的;我对除以3的预测是爆炸性的,但是对于5和7来说它是失败的。看看它们的相关程度是多么有趣;但我无法解释
我在分析编译器在分割时的作用时出错了什么?
公式生成器的代码(通过 kol ):

var $a = $("#a"),
$m = $("#m"),
$generate = $("#generate"),
$formula = $("#formula");

$generate.click(function () {
var a = +$a.val(),
    m = +$m.val(),
    m2 = 0,
    k = 0;

if (isNaN(a) || a !== Math.floor(a) || a < 2 || a > Math.pow(2, 31) - 1) {
    alert("Divisor \"a\" must be an integer between 2 and 2^32-1");
    return;
}
m2 = Math.pow(2, m);
k = Math.ceil(m2 / a);
$formula.html("<i>n</i> / " + a + " = (" + k + " * <i>n</i>) &gt;&gt; " + m + "; for all <i>n</i> &lt; " + m2);
});

$generate.click();

1 个答案:

答案 0 :(得分:2)

不,C编译器不会通过按位操作进行arithemetics。即使在装配过程中,也不必按位操作进行化验。当然,它在处理器上以这种方式工作,并且处理器提供这里描述的命令:http://en.wikibooks.org/wiki/X86_Assembly/Arithmetic作为这些操作的抽象。