c – 如何在没有未定义行为的情况下安全地偏移位?
我正在编写一个函数,它将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() ); } } 而“测试程序”: 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>> 4是0xF89A而不是预期的0x089A. int16_t val = 0x89A0; // 1000100110100000 val = val >> 4; // 2222200010011010 我不明白为什么>>运算符有时会插入0,有时为1.我无法找到如何安全地执行函数的最终操作(result = result>> missingbits;在某些时候一定是错的…) 解决方法
这是因为移位是一个算术运算,它将操作数提升为int,这将进行符号扩展.
即将带符号的16位整数(int16_t)0x89a0提升为32位有符号整数(int)会导致该值变为0xffff89a0,这是移位的值. 参见例如this arithmetic operation conversion reference获取更多信息. 您应该将变量(或值)转换为无符号整数(例如,在您的情况下为uint16_t): val = static_cast<uint16_t>(val) >> 4; 如果类型不是真的知道,如果它是模板参数,那么你可以使用 val = static_cast<typename std::make_unsigned<T>::type>(val) >> 4; (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |