【问题标题】:Fast bit concatenation reversing one of arguments快速位连接反转参数之一
【发布时间】:2019-03-01 13:04:42
【问题描述】:

我需要将两个变量合并为一个,如下例所示:

x = 0b110101
y = 0b10011

merged(x,y) -> 0b11010111001
merged(y,x) -> 0b10011101011

因此,y 被反转并连接到 x,没有不必要的零(结果的最后一位始终为 1)

换句话说:merged(abcdefg, hijklm) 导致abcdefgmlkjih 其中ah1

在 C 中最有效的方法是什么

编辑:最有效 = 最快,sizeof(x) = sizeof(y) = 1CHAR_BIT= 8

EDIT2:由于问题已被搁置,我将在此处发布摘要: 更多限制和更多细节:

目标语言:支持 64 位操作的 C

对于这个确切的问题,最快的方法是 cmets 中建议的 256 元素查找表,它返回高位索引 N 和第二个参数的反转值,所以我们可以将第一个参数向左移动 @987654340 @ 并按位执行 or 与第二个参数的相反值。

如果有人需要为大于 8 位的参数提供良好的性能(查找表不是一个选项),他们可以:

  1. 使用 de Bruijn 算法查找高位索引。 32 位示例:

    uint32_t msbDeBruijn32( uint32_t v )
    {
        static const int MultiplyDeBruijnBitPosition[32] =
        {
            0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
            8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
        };
    
        v |= v >> 1; // first round down to one less than a power of 2
        v |= v >> 2;
        v |= v >> 4;
        v |= v >> 8;
        v |= v >> 16;
    
        return MultiplyDeBruijnBitPosition[( uint32_t )( v * 0x07C4ACDDU ) >> 27];
    }
    

取自这个问题: Find most significant bit (left-most) that is set in a bit array

  1. 使用此位旋转技巧来反转第二个参数的字节位(一个接一个)

    unsigned char b; // reverse this (8-bit) byte    
    b = (b * 0x0202020202ULL & 0x010884422010ULL) % 1023;  
    

取自这里:http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious

所以,这个问题现在可以关闭/删除了

【问题讨论】:

  • 可移植代码中最棘手的部分是在 y 中找到最高 1 位。
  • 效率最高?就像使用更少的时间、更少的内存、更少的 CPU、更少的指令,......我猜的 everything 更少?我建议查找数组:)
  • 查看stackoverflow.com/questions/53161/…以查找最高位
  • @user3386109 如果您的年龄足以记住 64K 查找表何时,请举手。 ✋
  • @Barmar 那么就没有必要标记它C++。无论如何,这不是比萨店;我们不会订购我们最喜欢的风格的解决方案,我们会询问我们目前正在编程的语言中的特定问题。

标签: c bit-manipulation


【解决方案1】:

解决方案

这应该可以在 C++ 和 C 中解决问题:

unsigned int merged(unsigned char a, unsigned char b) {
    unsigned int r = a; 
    for (; b; b>>=1) 
        r = (r<<1)|(b&1);
    return r;
}

我认为没有更有效的方法可以做到这一点。这里是online demo

它是如何工作的?

这通过从第一个参数开始构建结果来工作:

 r is 0b110101; 

然后是left-shifts r,也就是使用您的术语,例如在 r 的最低端连接一个 0:

 0b110101 << 1 is  0b1101010; 

同时我们使用bitwise and 得到b 的最低位,然后使用bitwise or 将r 的最低位设置为相同的值:

 0b10011 & 1 is 0b00001
 so (0b110101 << 1)|(0b10011 & 1) is 0b1101011;

然后我们将 b 右移,以同样的方式处理下一个最低位:

 0b10011 >> 1 is 0b1001; 

只要 b 中还有一些位设置。

重要提示

您必须小心使用的类型以避免溢出的风险。因此,对于 2 unsigned char 作为输入,您可以使用 unsigned int 作为输出。

请注意,如果左移可能导致符号位发生变化,则使用有符号整数会有风险。这就是unsigned 在这里很重要的原因。

【讨论】:

  • 按照要求使用 x, y 代替 a, b 会很好;您还可以使用 a 本身删除声明 r;然而编译器会优化它。
  • @TomKuschel 我没有使用 a 本身,因为我的想法是最终促进使用比输入参数更大的返回类型,以避免设计上的溢出风险(请参阅部分(“重要备注”)。编译器无论如何都会进行优化。我没有使用 x 和 y,因为在 OP 的示例中,它们是用作参数的变量而不是函数参数(OP 使用了合并(x,y)和合并(y,x) ,所以使用 a,b 是中性的)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-30
  • 1970-01-01
  • 1970-01-01
  • 2018-08-22
  • 1970-01-01
  • 2016-01-04
  • 1970-01-01
相关资源
最近更新 更多