如何安全地偏移位而没有未定义的行为?

jpo*_*o38 6 c++ std-bitset

我正在编写一个函数,它将bitset转换为int/uint值,因为bitset可能比目标类型的位数少.

这是我写的函数:

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() );
    }
}
Run Code Online (Sandbox Code Playgroud)

而"测试程序":

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
Run Code Online (Sandbox Code Playgroud)

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

使用uint16目标类型时,请参阅该程序,如下所示:

uint16_t val = 0x89A0; // 1000100110100000
val = val >> 4;        // 0000100010011010
Run Code Online (Sandbox Code Playgroud)

但是,它在使用时失败int16_t,因为0x89A0 >> 40xF89A不是预期的0x089A.

int16_t val = 0x89A0; // 1000100110100000
val = val >> 4;       // 1111100010011010
Run Code Online (Sandbox Code Playgroud)

我不明白为什么>>操作符有时会插入0,有时1.我无法找到如何安全地执行我的函数的最终操作(result = result >> missingbits;在某些时候必定是错误的...)

Som*_*ude 4

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

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

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

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

val = static_cast<uint16_t>(val) >> 4;
Run Code Online (Sandbox Code Playgroud)

如果类型不是很清楚,比如它是模板参数,那么您可以使用std::make_unsigned

val = static_cast<typename std::make_unsigned<T>::type>(val) >> 4;
Run Code Online (Sandbox Code Playgroud)