有没有办法使用bit twiddling“截断”一个整数,就像它被分层然后再乘以一样,如:
z = floor(x / y) * y
我知道如果y
具有2的幂,则可以这样做,例如:
z = floor(x / 4) * 4 == x & ~3
但是当y
是一般的正整数时,会使用什么技巧?
答案 0 :(得分:1)
对于每个人y
,有一系列操作(加法,减法和二进制移位),它将x
除以y
比(x86)除法指令更快。
然而,找到该序列并不简单,必须提前完成(当你用相同的y
划分时可行)。
一个简单的例子:将任意uint32
x
除以3,我们可以在x * M
类型中计算uint64
并将其向右移33位,其中M
是一个魔术常数,等于2 33 / 3舍入向上。
以下代码(C)使用上述算法尝试20个随机uint32
值,并检查结果是否等于仅除以3:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main ()
{
int step;
unsigned x, y1, y2;
unsigned const M = (1ULL << 33) / 3 + 1;
srand (time (NULL));
for (step = 0; step < 20; step++)
{
x = (rand () << 30) | (rand () << 15) | rand ();
y1 = x / 3;
y2 = (x * 1ULL * M) >> 33;
printf ("%10u %10u %10u %s\n", x, y1, y2, y1 == y2 ? "true" : "false");
}
return 0;
}
有关详细信息,请参阅Hacker's Delight一般书籍和免费提供的附加内容 - 第10章:hackersdelight.org/divcMore.pdf。
答案 1 :(得分:0)
这适用于2的幂是二进制表示的工作方式。除以2(或2的幂)与位移相同。向前移动然后向左移动相同的数量与你所说的地板划分相同。
考虑任意二进制数:110101010111。如果你将它向右移3次(除以8),然后再向后移动它将转向110101010000,这与用111111111000进行运算相同。现在让我们考虑除以(十进制)数字16的3除以:以10000开始。除以3的除法(5)将是5(101)并再次乘以3是15(1111)。没有位移可以做到这一点。
答案 2 :(得分:0)
显而易见的事情是转换为你想要使用的任何基数,然后基本上使最后一个数字为0.(或者如果你使用k次幂,那么最后的k个数字为0)。但是你询问了bit(base-2)操作。事实证明,对于任何所需的基数B(至少,这是奇数),你可以得到一个二进制数,这样基数B中的前M个数字就是你想要的任何东西。因此,你怎么能可能有一个通用的方法,你想要的(有一个奇数基数),只适用于位(二进制)?至少它可能比简单地将您的数字转换为您想要的基数并将许多最后的数字设置为0然后转换回自然的基数2整数表示要复杂得多。