我正在编写一个函数,将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() );
}
}
&#34;测试程序&#34;:
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; // 1111100010011010
我不明白为什么&gt;&gt;运算符有时会插入0,有时是1.我无法找到如何安全地执行函数的最终操作(result = result >> missingbits;
在某些时候必定是错误的...)
答案 0 :(得分:4)
这称为arithmetic shifting。在签名类型上,最重要的位是符号位。向右移动负值时,高位设置为1,结果仍为负数。 (结果是除以2 n ,其中n是移位的位数,向负无穷大舍入)。
为避免这种情况,请使用无符号类型。移动它们使用logical shifting,它将高位设置为0.
更改此行:
result = result >> missingbits;
到
result = static_cast<T>(static_cast<uintmax_t>(result) >> missingbits);
(uintmax_t
是编译器支持的最大宽度无符号整数类型)
或使用std::make_unsigned
作为Joachim Pileborg在他的回答中写道。
答案 1 :(得分:4)
因为移位是一个算术运算,将操作数提升为int
,这将进行符号扩展。
即。将带符号的16位整数(int16_t
)0x89a0
提升为32位有符号整数(int
)会使该值变为0xffff89a0
,这是一个值移位。
参见例如this arithmetic operation conversion reference了解更多信息。
您应该将变量(或值)转换为无符号整数(例如uint16_t
):
val = static_cast<uint16_t>(val) >> 4;
如果该类型不是真的知道,就好像它是一个模板参数,那么你可以使用std::make_unsigned
:
val = static_cast<typename std::make_unsigned<T>::type>(val) >> 4;
答案 2 :(得分:1)
如上所述,当您的类型为signed
时,>>
运算符正在执行算术移位。因此,除了上面建议的解决方案之外,如果您需要进行逻辑转换,您可以随时使用mask
,如下所示:
int mask = 1 << (targetSize-missingbits-1);
mask |= mask - 1;
result = (result >> missingbits) & mask;
在这种情况下,mask
将为您提供missingbits
MSB&#39; 0
和其他1
。在您的情况下,4个MSB将为0
,其余为1
。然后,执行&
操作会重置missingbits
中的第一个result
,这就是您所需要的:
0xF89A & 0x0FFF = 0x089A
看到它正常工作live-example。
答案 3 :(得分:1)
带循环的原始代码对我来说对我来说有点复杂,我会这样写(我的意思是作为第二个选项,在我以某种方式莫名其妙地无法完全避免使用#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;
}
和模板之后,对于像数据位大小调整这样简单的事情,首先):
# /usr/local/etc/mongod.conf
net:
bindIp: 127.0.0.1