我需要在2 uint64_t
交错位中uint32_t
:如果A=a0a1a2...a31
和B=b0b1...b31
,我需要C = a0b0a1b1...a31b31
。有没有办法有效地做到这一点?到目前为止,我只有一个带有for
循环的32次迭代的天真方法,其中每次迭代都C|=((A&(1<<i))<<i)|((B&(1<<i))<<(i+1))
。
我想应该有一些数学技巧,例如将A和B乘以一些特殊数字,这导致在得到的64位数字中将它们的位与零交错,这样只留下or
这些产品。但我找不到这样的乘数。
另一种可能的方法是编译器内部或汇编指令,但我不知道。
答案 0 :(得分:7)
NathanOliver的链接提供了16位 - &gt; 32位实现:
static const unsigned int B[] = {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF};
static const unsigned int S[] = {1, 2, 4, 8};
unsigned int x; // Interleave lower 16 bits of x and y, so the bits of x
unsigned int y; // are in the even positions and bits from y in the odd;
unsigned int z; // z gets the resulting 32-bit Morton Number.
// x and y must initially be less than 65536.
x = (x | (x << S[3])) & B[3];
x = (x | (x << S[2])) & B[2];
x = (x | (x << S[1])) & B[1];
x = (x | (x << S[0])) & B[0];
y = [the same thing on y]
z = x | (y << 1);
适用于:
即。它继续如下:
abcdefghijklmnop
-> 00000000abcdefgh 00000000ijklmnop
-> 0000abcd0000efgh 0000ijkl0000mnop
-> 00ab00cd00ef00gh 00ij00kl00mn00op
-> 0a0b0c0d0e0f0g0h 0i0j0k0l0m0n0o0p
然后将两个输入组合在一起。
根据我之前的评论,要将其扩展到64位,只需将初始移位加16并按0x0000ffff0000ffff
加掩码,或者因为您可以直观地遵循该模式或作为分而治之的步骤,将32位问题转换为两个非重叠的16位问题,然后使用16位解决方案。
答案 1 :(得分:5)
对于较大的整数,值得一提的是clmul x86扩展,用于有限域乘法(无位乘法)。整数与零交织等效于整数与其自身的无符号乘法,这是一条ALU指令。
答案 2 :(得分:0)
一个简短的,预先计算的数组查找是否算作“数学技巧”?
预先计算256 uint16_t
s:
static const uint16_t lookup[256]={0x0000, 0x0001, 0x0005 ..., 0x5555};
我们可以交错两个8位值,并轻松得出16位值:
uint16_t interleave(uint8_t a, uint8_t b)
{
return (lookup[a] << 1) | lookup[b];
}
如何扩展它以将两个32位值交织成64位值应该是显而易见的:对于构成uint32_t
的四个字节中的每一个,调用这四次,然后{{1将<<
结果放在一起。贿赂编译器内联整个事情,最终结果应该相当快速和便宜。
由于RAM目前很便宜,您可能还需要考虑预先计算的65536 |
s表。