6

考虑到 bitset 的位数可能少于目标类型,我正在编写一个将 bitset 转换为 int/uint 值的函数。

这是我写的函数:

template <typename T,size_t count> static T convertBitSetToNumber( const std::bitset<count>& bitset )
{
    T result;
    #define targetSize (sizeof( T )*CHAR_BIT)
    if ( targetSize > count )
    {
        // if bitset is 0xF00, converting it as 0x0F00 will lose sign information (0xF00 is negative, while 0x0F00 is positive)
        // This is because sign bit is on the left.
        // then, we need to add a zero (4bits) on the right and then convert 0xF000, later, we will divide by 16 (2^4) to preserve sign and value

        size_t missingbits = targetSize - count;

        std::bitset<targetSize> extended;
        extended.reset(); // set all to 0
        for ( size_t i = 0; i != count; ++i )
        {
            if ( i < count )
                extended[i+missingbits] = bitset[i];
        }

        result = static_cast<T>( extended.to_ullong() );

        result = result >> missingbits;

        return result;
    }
    else
    {
        return static_cast<T>( bitset.to_ullong() );
    }
}

和“测试程序”:

uint16_t val1 = Base::BitsetUtl::convertBitSetToNumber<uint16_t,12>( std::bitset<12>( "100010011010" ) );
// val1 is 0x089A
int16_t val2 = Base::BitsetUtl::convertBitSetToNumber<int16_t,12>( std::bitset<12>( "100010011010" ) );
// val2 is 0xF89A

注意:请参阅与 Ped7g 的评论/交换,上面的代码是正确的并保留位符号,并对有符号或无符号位进行 12->16 位转换。但是,如果您正在研究如何在签名对象上将 0xABC0 偏移到 0x0ABC,那么答案可以帮助您,所以我不会删除这个问题。

uint16在用作目标类型时查看程序的工作原理,如下所示:

uint16_t val = 0x89A0; // 1000100110100000
val = val >> 4;        // 0000100010011010

但是,使用时失败int16_t,因为0x89A0 >> 4is0xF89A而不是 expected 0x089A

int16_t val = 0x89A0; // 1000100110100000
val = val >> 4;       // 1111100010011010

我不明白为什么 >> 运算符有时会插入 0 有时会插入 1。而且我不知道如何安全地执行我的函数的最终操作(result = result >> missingbits;在某些时候一定是错误的......)

4

4 回答 4

4

这称为算术移位。在有符号类型上,最高有效位是符号位。当您将负值向右移动时,高位设置为 1,因此结果仍然是负数。(结果是除以 2 n,其中 n 是移位的位数,四舍五入到负无穷大)。

为避免这种情况,请使用无符号类型。移位它们使用逻辑移位,它将高位设置为 0。

更改此行:

result = result >> missingbits;

result = static_cast<T>(static_cast<uintmax_t>(result) >> missingbits);

uintmax_t是编译器支持的最大宽度无符号整数类型)

或使用std::make_unsignedJoachim Pileborg 在他的回答中写道。

于 2016-09-22T14:21:45.433 回答
4

这是因为移位是一种算术运算,它将操作数提升int,这将进行符号扩展。

即将带符号的 16 位整数 ( int16_t)0x89a0提升为 32 位带符号整数 ( int) 会导致值变为0xffff89a0,即被移位的值。

有关更多信息,请参见例如此算术运算转换参考

您应该将变量(或值)转换为无符号整数(即uint16_t在您的情况下):

val = static_cast<uint16_t>(val) >> 4;

如果类型不是真的知道,比如它是一个模板参数,那么你可以使用std::make_unsigned

val = static_cast<typename std::make_unsigned<T>::type>(val) >> 4;
于 2016-09-22T14:23:33.057 回答
1

带有循环的原始代码对我来说看起来有点复杂,我会这样写(我的意思是作为第二个选项,在我莫名其妙地无法完全避免使用std::bitset和模板之后,对于像位大小调整这样简单的事情首先是数据):

#include <bitset>
#include <climits>

template <typename T,size_t count> static T convertBitSetToNumber( const std::bitset<count>& bitset )
{
    constexpr size_t targetSize = sizeof( T )*CHAR_BIT;
    if (targetSize == count) return static_cast<T>(bitset.to_ullong());
    if (targetSize < count) return static_cast<T>(bitset.to_ullong() >> (count - targetSize));
    return static_cast<T>(bitset.to_ullong() << (targetSize - count)) >> (targetSize - count);
}

// Example test producing from 0x089A bitset unsigned/signed values:
// 16b: 89a f89a | 8b: 89 89 | 32b: 89a fffff89a

#include <iostream>

int main()
{
    const std::bitset<12> testBitset("100010011010");
    std::hex(std::cout);
    std::cout << convertBitSetToNumber<uint16_t,12>( testBitset ) << std::endl;
    std::cout << convertBitSetToNumber<int16_t,12>( testBitset ) << std::endl;
    std::cout << (0xFF & convertBitSetToNumber<uint8_t,12>( testBitset )) << std::endl;
    std::cout << (0xFF & convertBitSetToNumber<int8_t,12>( testBitset )) << std::endl;
    std::cout << convertBitSetToNumber<uint32_t,12>( testBitset ) << std::endl;
    std::cout << convertBitSetToNumber<int32_t,12>( testBitset ) << std::endl;
}
于 2016-09-22T15:53:15.633 回答
1

如前所述,当您的类型是运算符时signed>>运算符正在执行算术移位。因此,除了上面建议的解决方案之外,如果您需要进行逻辑移位,您始终可以简单地使用 a mask,如下所示:

    int mask = 1 << (targetSize-missingbits-1);
    mask |= mask - 1;
    result = (result >> missingbits) & mask;

在这种情况下,mask将为您提供missingbitsMSB0和其余部分1。在您的情况下,将有 4 个 MSB 0,其余为1. 然后,执行&操作将重置missingbits您的第一个,result这就是您需要的:

0xF89A & 0x0FFF = 0x089A

看到它的工作实例

于 2016-09-22T14:48:34.473 回答