我在一个64位寄存器的下部有一个32位的值;顶部是0。让< code>X表示一个包含信息的位,其位从LSB到MSB排列,如下所示:
X X X ... X 0 0 0 0 ... 0
现在,我想用信息"间隔"这些位,这样我就有了
X 0 X 0 X 0 ... X 0
(或者如果你宁愿把0放在第一位,那么
0 X 0 X 0 X 0 ... X
也很好。)
有什么快速的方法可以做到这一点?
一个与多CPU架构相关的答案会很好,但一些特定于英特尔x86_64和/或nVIDIA Pascal SM的答案会是最相关的。
这里有一种结合初始移位和选择性加法的替代方法:
#include <stdint.h>
uint64_t expand_32_64(uint64_t x) {
x = ((x & 0xFFFF0000) << 16) | (x & 0x0000FFFF);
x = ((x & 0xFF000000FF00) << 8) | (x & 0x00FF000000FF);
x = ((x & 0xF000F000F000F0) << 4) | (x & 0x0F000F000F000F);
x += x & 0x0808080808080808;
x += x & 0x1414141414141414;
x += x & 0x2A2A2A2A2A2A2A2A;
return x + x; /* remove the last addition for expand_32_63 */
}
这可能不是最有效或最优雅的解决方案,但它是最容易理解的 IMO:
for (int i = 31; i >=0; i--) {
reg |= ((reg >> i) & 1) << 2*i;
reg &= ~(1 << i)
}
这会将 00000000ABCDEFGH 转换为 0A0B0C0D0E0F0G0H
(实际上,该示例是一个 16 位数字,您可以将 31
替换为 7
。但你知道我的意思)如果你想要右边的零,
A0B0C0D0E0F0G0H0
,将 2*
i 替换为 2*i 1
。
这就是众所周知的莫顿数,它是并行扩展的一个特例,在下面的问题中,它反过来又是右压缩的逆运算
一个通用的解决方案可能是
uint64_t bit_expand(uint64_t x)
{
// Input: 00000000ABCDEFGH, each character is a nibble
x = ((x & 0xFFFF0000) << 32) | ((x & 0x0000FFFF) << 16);
// Result: ABCD0000EFGH0000
x = (x & 0xFF000000FF000000) | ((x & 0x00FF000000FF0000) >> 8);
// Result: AB00CD00EF00GH00
x = (x & 0xF000F000F000F000) | ((x & 0x0F000F000F000F00) >> 4);
// Result: A0B0C0D0E0F0G0H0. Each byte: abcd0000
x = (x & 0xC0C0C0C0C0C0C0C0) | ((x & 0x3030303030303030) >> 2);
// Result: Each byte: ab00cd00
x = (x & 0x8888888888888888) | ((x & 0x4444444444444444) >> 1);
// Result: Each byte: a0b0c0d0
return x;
}
然而,常量生成在RISC体系结构上可能是低效的,因为64位立即数不能像在x86上那样存储在单个指令中。即使在x86上,输出程序集也很长。这里是另一个可能的实现,如比特扭曲黑客所描述的
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 = (y | (y << S[3])) & B[3];
y = (y | (y << S[2])) & B[2];
y = (y | (y << S[1])) & B[1];
y = (y | (y << S[0])) & B[0];
z = x | (y << 1);
也可以使用查找表
#define EXPAND4(a) ((((a) & 0x8) << 4) | (((a) & 0x4) << 2) \
| (((a) & 0x2) << 1) | (((a) & 0x1)))
const uint8_t LUT[16] = {
EXPAND4( 0), EXPAND4( 1), EXPAND4( 2), EXPAND4( 3),
EXPAND4( 4), EXPAND4( 5), EXPAND4( 6), EXPAND4( 7),
EXPAND4( 8), EXPAND4( 9), EXPAND4(10), EXPAND4(11),
EXPAND4(12), EXPAND4(13), EXPAND4(14), EXPAND4(15)
};
output = ((uint64_t)LUT[(x >> 28) & 0xF] << 56) | ((uint64_t)LUT[(x >> 24) & 0xF] << 48)
| ((uint64_t)LUT[(x >> 20) & 0xF] << 40) | ((uint64_t)LUT[(x >> 16) & 0xF] << 32)
| ((uint64_t)LUT[(x >> 12) & 0xF] << 24) | ((uint64_t)LUT[(x >> 8) & 0xF] << 16)
| ((uint64_t)LUT[(x >> 4) & 0xF] << 8) | ((uint64_t)LUT[(x >> 0) & 0xF] << 0);
如果需要,可以增加查找表的大小
在带有BMI2的x86上,有PDEP指令的硬件支持,可以通过以下内部
output = _pdep_u64(x, 0xaaaaaaaaaaaaaaaaULL);
另一种无位存储/扩展指令但具有快速乘法器的架构解决方案
uint64_t spaceOut8bits(uint8_t b)
{
uint64_t MAGIC = 0x8040201008040201;
uint64_t MASK = 0x8080808080808080;
uint64_t expand8bits = htobe64(((MAGIC*b) & MASK) >> 7);
uint64_t spacedOutBits = expand8bits*0x41041 & 0xAA000000AA000000;
return (spacedOutBits | (spacedOutBits << 24)) & 0xFFFF000000000000;
}
uint64_t spaceOut64bits(uint64_t x)
{
return (spaceOut8bits(x >> 24) >> 0)
| (spaceOut8bits(x >> 16) >> 16)
| (spaceOut8bits(x >> 8) >> 32)
| (spaceOut8bits(x >> 0) >> 48);
}
它的工作方式是这样的
abcdefgh
扩展到 a0000000 b0000000 c0000000 d0000000 e0000000 f0000000 g0000000 h0000000 并存储在 expand8 位中
间隔OutBits
将包含a0b0c0d0 000000000 000000000 00000000 e0f0g0h0 000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000我们将结果中的两个字节合并在一起使比特更接近的神奇数字是这样计算的
a0000000b0000000c0000000d0000000e0000000f0000000g0000000h0000000
× 1000001000001000001
────────────────────────────────────────────────────────────────
a0000000b0000000c0000000d0000000e0000000f0000000g0000000h0000000
00b0000000c0000000d0000000e0000000f0000000g0000000h0000000
+ 0000c0000000d0000000e0000000f0000000g0000000h0000000
000000d0000000e0000000f0000000g0000000h0000000
────────────────────────────────────────────────────────────────
a0b0c0d0b0c0d0e0c0d0e0f0d0e0f0g0e0f0g0h0f0g0h000g0h00000h0000000
& 1010101000000000000000000000000010101010000000000000000000000000
────────────────────────────────────────────────────────────────
a0b0c0d0000000000000000000000000e0f0g0h0000000000000000000000000
这里可以看到输出组件。您可以更改编译器,看看它在各种架构上是如何工作的
在Bit Twiddling Hacks页面上也有另一种方法
z = ((x * 0x0101010101010101ULL & 0x8040201008040201ULL) *
0x0102040810204081ULL >> 49) & 0x5555 |
((y * 0x0101010101010101ULL & 0x8040201008040201ULL) *
0x0102040810204081ULL >> 48) & 0xAAAA;
可以在不使用BMI2的情况下找到PDEP的便携式高效替代品中可以找到更多解决方案?
相关:如何对像素数据进行位条带化?
如您所见,如果没有位存款指令的可用性,在操作方面将非常复杂。如果您执行非像这样的位条带化,那么最好使用 SIMD 并行执行
问题内容: 我很好奇如何快速进行元组的for循环。 我知道要访问每个成员,您可以使用带点号的索引号 //错误:类型不符合协议顺序 问题答案: 是的你可以! 瞧! 注意最后一个是不是一个元组这样什么也不会发生(尽管它是一个内容可以被访问1元组或“单” ,为0)。 有趣的是,它甚至可以迭代其他类型的集合。 并且该集合包括和! 注意:作为块的第二个参数传递的值是type 。您必须将其强制转换回原始类型的
所以我做了这个程序,你可以给出一个圆或一条线的参数,它会通过在显示器上画一个数组来显示所述对象。 我知道我的代码可能优化得很差,但我一周前才开始编程。所以如果代码很难理解,请原谅我。 提前谢谢!
我公司使用汇流维基,这里有一些文档和共享记录。但是我发现这不是很方便,例如插入一些代码块,我必须键入然后选择code block然后选择language(例如bash java等),如果使用markdown就。 有没有什么方式可以在汇合中快速插入代码?
材料设计非常强调“纸张”的隐喻。要做到这一点,阴影是必不可少的。由于材料设计是一种理念,而不是API(尽管它内置在L中),因此应该在任何地方(Windows窗体、HTML/CSS等)进行设计。如何在Android API 14到20中做到这一点? 请注意,对于圆形和其他非方形形状,预制PNG实际上并不实用。
问题内容: 我知道Internet Explorer具有自动换行样式,但是我想知道是否有跨浏览器的方法可以对div中的文本进行这种操作。 最好是CSS,但JavaScript代码片段也可以正常工作。 编辑:是的,指的是长长的弦,为人们加油! 问题答案: 阅读原始评论时,卢瑟福正在寻找一种 跨浏览器的 方式来包装 不间断的 文本(根据他对IE的自动换行设计,旨在破坏不间断的字符串)。 我现在已经使用
我找不到的是如何在JetBrains WebStorm中更改这个设置(我已经尝试过在他们的网站和首选项等上进行搜索,但没有成功)--我也运行了很多搜索(我的google-foo太糟糕了!)但尚未找到如何更改此设置。 有人知道如何(或者如果?)可以更改此设置吗?
问题内容: 在PHP中,有没有一种简单的方法可以将数字转换为单词?举例来说, 27 到 27 。 问题答案: 我在网上找到了一些(2007/2008)源代码,它具有版权,但是我可以自由使用它,并根据需要对其进行修改,因此将其放在此处并在CC-Wiki下重新许可: