拉伸面具 - 位操纵

时间:2017-02-06 16:00:10

标签: c++ bit-manipulation

我想拉伸一个掩码,其中每个位代表4位拉伸掩码。 我正在寻找一个优雅的位操作来使用c ++和systemC进行拉伸

例如:

输入:

mask (32 bits) = 0x0000CF00

输出:

stretched mask (128 bits) = 0x00000000 00000000 FF00FFFF 00000000

只是为了澄清这个例子让我们看一下字节C:

0xC = 1100 after stretching: 1111111100000000 = 0xFF00

5 个答案:

答案 0 :(得分:3)

以优雅的形式做到这一点并不容易。 简单模式可能是创建一个带有移位位的循环

sc_biguint<128> result = 0;
for(int i = 0; i < 32; i++){
    if(bit_test(var, i)){
        result +=0x0F;
    }
    result << 4;
}

答案 1 :(得分:3)

这是一种将16位掩码扩展为64位的方法,其中每位代表4位拉伸掩码:

uint64_t x = 0x000000000000CF00LL;

x = (x | (x << 24)) & 0x000000ff000000ffLL;
x = (x | (x << 12)) & 0x000f000f000f000fLL;
x = (x | (x << 6)) & 0x0303030303030303LL;
x = (x | (x << 3)) & 0x1111111111111111LL;
x |= x << 1;
x |= x << 2;

从底部16位的掩码开始。然后它将掩码的前8位移动到前32位,如下所示:

0000000000000000 0000000000000000 0000000000000000 ABCDEFGHIJKLMNOP

成为

0000000000000000 00000000ABCDEFGH 0000000000000000 00000000IJKLMNOP

然后它解决了将掩码从32位字的底部8位拉伸到顶部和底部32位的类似问题:

000000000000ABCD 000000000000EFGH 000000000000IJKL 000000000000MNOP

然后它在16内进行4位,依此类推,直到比特分散开来:

000A000B000C000D 000E000F000G000H 000I000J000K000L 000M000N000O000P

然后它&#34;涂抹&#34;它们通过对结果进行两次OR运算得到4位:

AAAABBBBCCCCDDDD EEEEFFFFGGGGHHHH IIIIJJJJKKKKLLLL MMMMNNNNOOOOPPPP

您可以通过添加额外的第一步将其扩展到128位,其中您将移位48位并使用128位常量进行掩码:

x = (x | (x << 48)) & 0x000000000000ffff000000000000ffffLLL;

您还必须通过重复位模式将其他常量拉伸到128位。但是(据我所知),没有办法在C ++中声明128位常量,但也许你可以用宏或其他东西(see this question)来实现。您也可以分别在顶部和底部16位使用64位版本制作128位版本。

如果加载掩蔽常数结果是一个困难或瓶颈,你可以使用移位和掩蔽从前一个生成每个:

uint64_t m = 0x000000ff000000ffLL;

m &= m >> 4; m |= m << 16;  // gives 0x000f000f000f000fLL
m &= m >> 2; m |= m << 8;  // gives 0x0303030303030303LL
m &= m >> 1; m |= m << 4; // gives 0x1111111111111111LL

答案 2 :(得分:2)

这对你有用吗?

#include <stdio.h>

long long Stretch4x(int input)
{
    long long output = 0;

    while (input & -input)
    {
        int b = (input & -input);
        long long s = 0;
        input &= ~b;
        s = b*15;
        while(b>>=1)
        {
            s <<= 3;
        }

        output |= s;
    }
    return output;  
}

int main(void) {
    int input = 0xCF00;

    printf("0x%0x ==> 0x%0llx\n", input, Stretch4x(input));
    return 0;
}

<强>输出

0xcf00 ==> 0xff00ffff00000000

答案 3 :(得分:2)

其他解决方案都很好。但是,大多数它们比C ++更多。这个解决方案很简单:它使用std::bitset并为每个输入位设置4位。

#include <bitset>
#include <iostream>

std::bitset<128> 
starch_32 (const std::bitset<32> &input)
{
    std::bitset<128> output;

    for (size_t i = 0; i < input.size(); ++i) {
        // If `input[N]` is `true`, set `output[N*4, N*4+4]` to true.
        if (input.test (i)) {
            const size_t output_index = i * 4;

            output.set (output_index);
            output.set (output_index + 1);
            output.set (output_index + 2);
            output.set (output_index + 3);
        }
    }

    return output;
}

// Example with 0xC. 
int main() {
    std::bitset<32> input{0b1100};

    auto result = starch_32 (input);

    std::cout << "0x" << std::hex << result.to_ullong() << "\n";
}

Try it online!

答案 4 :(得分:0)

在x86上,您可以使用PDEP intrinsic将16个掩码位移动到64位字的正确半字节(例如,每个半字节的低位),然后使用几个shift +或将它们涂抹在其余部分:

unsigned long x = _pdep_u64(m, 0x1111111111111111);
x |= x << 1;
x |= x << 2;

您也可以通过0xF的单次乘法替换这两个OR和两个移位,这样可以完成相同的拖尾操作。

最后,您可以考虑使用SIMD方法:上面的samgak等解决方案应该自然地映射到SIMD。