如何处理比64位更长的位域?

7
问题已经说得很清楚了。
如果我有一个96位的字段:
uint32_t flags[3]; //(thanks @jalf!)

如果我的子字段可能跨越32位边界(例如,从第29位到第35位运行的字段),那么我应该如何最好地访问它?

我需要尽可能快地访问它们,因此我不想按32位数组元素进行迭代。


您想在位域上执行哪些操作? - alanxz
如果你恰好在一台安装了gcc、clang或其他编译器的64位机器上,你可以直接使用它们的128位数据类型__uint128_t - Jens Gustedt
@JensGustedt 我曾考虑过这一点,但我需要恰好96位来压缩数据并获得更好的缓存性能(在缓存中,那最后32位只会浪费)。 - Engineer
3个回答

11

STL 包含一个用于处理任意长度的位域(bitfields)的类:

#include <bitset>

int main() {
  const bitset<12> mask(2730ul); 
  cout << "mask =      " << mask << endl;

  bitset<12> x;

  cout << "Enter a 12-bit bitset in binary: " << flush;
  if (cin >> x) {
    cout << "x =        " << x << endl;
    cout << "As ulong:  " << x.to_ulong() << endl;
    cout << "And with mask: " << (x & mask) << endl;
    cout << "Or with mask:  " << (x | mask) << endl;
  }
}

在C++开发中,std::bitset就是你问题的答案。 - Simone
谢谢,但不是我在询问哪种数据结构要使用。每秒投射数亿个光线时,我无法承受标准容器的开销。 - Engineer
2
@NickWiggill - 你有什么理由认为使用std::bitset比替代方案慢吗?请考虑这段代码,它编译成了这段汇编代码std::bitset的解决方案有三个内存加载、两个移位、一个“或”和一个“与”。手写的解决方案有三个加载、两个移位、一个“或”和两个“与”。 - Robᵩ
2
@Rob 是的,我测试过了。鉴于这个程序的应用,堆栈耗尽和缓存性能影响都是非常重要的问题。如果人们停止将错误的答案标记为正确的就好了... - Engineer
你可能想要对这个假设进行测试。现代编译器非常擅长穿透抽象层。(顺便说一句,现代CPU也没有直接支持位域;就像std::containers一样,它们被编译成原始的位操作代码)。 - MSalters
1
实际上,您使用的是uint32_t flags [3];,std :: bitset是struct {uint32_t flags [3];},您看到有什么区别吗? - fghj

6

[此答案适用于C语言(并且也适用于C++)。]

跨平台的方法是根据需要应用位掩码和位移。

因此,要从29到35(包括这两个数)获取您的字段:

  (flags[1] & 0xF)        << 3
| (flags[0] & 0xE0000000) >> 29  // The bitmask here isn't really needed, but I like symmetry!

显然,你可以编写一个类/函数/宏来自动化这个过程。

我还在消化这个内容,但这正是我想要的。谢谢您使用我的示例以代码形式提供答案。 - Engineer
1
我正在编写一个模板函数来检索任意位范围的位时,我意识到这是多余的。如果你编写一些内联函数,为每个字段编写一个函数,并将掩码和移位计数硬编码,那么你就完成了。 - Mark Ransom
@Mark:如果你有一个模板函数,你应该将其发布为答案。这肯定比为每个字段硬编码更通用,也比宏更安全。 - Oliver Charlesworth
@MarkRansom,感谢您的评论。两位,制作这些宏的原因是什么呢?无论如何,这是从C语言中遗留下来的吗? - Engineer
1
@NickWiggill,编写宏或函数可以提高代码的可读性和重用性,同时降低出错概率并增强可维护性。宏消除了函数调用的开销,但内联函数也能做到同样的事情,而且更加类型安全,易于阅读。你的问题标记了C++和C两种语言,而C语言没有内联函数。 - Mark Ransom
@Oli,我只完成了大约三分之一的从头编写。我已经有很长时间没有需要这样的东西了。 - Mark Ransom

-1

位域超过64位并没有问题。它们甚至可以比你的更大!

我看到的问题是,你正在访问跨越32位边界的成员。这是一个更大的问题。但说实话,在64位平台上,如果你使用long long来创建你的位域类型,那么你只需要在64位边界上小心谨慎。

如果你的动机是为了使你的访问“尽可能快”,那么在试图比编译器更聪明之前三思而后行。现在,他们可以进行优化,而你甚至没有意识到。我最好的建议是:保持你的代码易于阅读和理解。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接