有没有办法在不使用任何外部库(Boost 等)的情况下将 std::bitset<64> 转换为双精度?我正在使用一个位集来表示遗传算法中的基因组,我需要一种将一组位转换为双精度的方法。
3 回答
C++11之路:
union Converter { uint64_t i; double d; };
double convert(std::bitset<64> const& bs) {
Converter c;
c.i = bs.to_ullong();
return c.d;
}
编辑:如评论中所述,我们可以使用char*
别名,因为它未指定而不是未定义。
double convert(std::bitset<64> const& bs) {
static_assert(sizeof(uint64_t) == sizeof(double), "Cannot use this!");
uint64_t const u = bs.to_ullong();
double d;
// Aliases to `char*` are explicitly allowed in the Standard (and only them)
char const* cu = reinterpret_cast<char const*>(&u);
char* cd = reinterpret_cast<char*>(&d);
// Copy the bitwise representation from u to d
memcpy(cd, cu, sizeof(u));
return d;
}
仍然需要 C++11 to_ullong
。
大多数人都试图提供让您将位向量视为直接包含编码的 int 或 double 的答案。
我建议你完全避免这种方法。虽然它对于工作的某些定义确实“有效”,但它在整个地方引入了汉明悬崖。您通常希望您的编码安排事情,以便如果两个解码值彼此接近,那么它们的编码值也彼此接近。它还强制您使用 64 位精度。
我会手动管理转换。假设您要编码三个变量:x、y 和 z。例如,您的领域专业知识可以用来说明 -5 <= x < 5、0 <= y < 100 和 0 <= z < 1,其中 x 需要 8 位精度,y 需要 12 位, z 为 10 位。这为您提供了只有 30 位的总搜索空间。你可以有一个 30 位的字符串,将前 8 个作为编码 x,接下来的 12 个作为 y,最后 10 个作为 z。您还可以自由地对每一个进行灰码以移除汉明悬崖。
我个人过去做过以下事情:
inline void binary_encoding::encode(const vector<double>& params)
{
unsigned int start=0;
for(unsigned int param=0; param<params.size(); ++param) {
// m_bpp[i] = number of bits in encoding of parameter i
unsigned int num_bits = m_bpp[param];
// map the double onto the appropriate integer range
// m_range[i] is a pair of (min, max) values for ith parameter
pair<double,double> prange=m_range[param];
double range=prange.second-prange.first;
double max_bit_val=pow(2.0,static_cast<double>(num_bits))-1;
int int_val=static_cast<int>((params[param]-prange.first)*max_bit_val/range+0.5);
// convert the integer to binary
vector<int> result(m_bpp[param]);
for(unsigned int b=0; b<num_bits; ++b) {
result[b]=int_val%2;
int_val/=2;
}
if(m_gray) {
for(unsigned int b=0; b<num_bits-1; ++b) {
result[b]=!(result[b]==result[b+1]);
}
}
// insert the bits into the correct spot in the encoding
copy(result.begin(),result.end(),m_genotype.begin()+start);
start+=num_bits;
}
}
inline void binary_encoding::decode()
{
unsigned int start = 0;
// for each parameter
for(unsigned int param=0; param<m_bpp.size(); param++) {
unsigned int num_bits = m_bpp[param];
unsigned int intval = 0;
if(m_gray) {
// convert from gray to binary
vector<int> binary(num_bits);
binary[num_bits-1] = m_genotype[start+num_bits-1];
intval = binary[num_bits-1];
for(int i=num_bits-2; i>=0; i--) {
binary[i] = !(binary[i+1] == m_genotype[start+i]);
intval += intval + binary[i];
}
}
else {
// convert from binary encoding to integer
for(int i=num_bits-1; i>=0; i--) {
intval += intval + m_genotype[start+i];
}
}
// convert from integer to double in the appropriate range
pair<double,double> prange = m_range[param];
double range = prange.second - prange.first;
double m = range / (pow(2.0,double(num_bits)) - 1.0);
// m_phenotype is a vector<double> containing all the decoded parameters
m_phenotype[param] = m * double(intval) + prange.first;
start += num_bits;
}
}
请注意,出于对您来说可能无关紧要的原因,我没有使用位向量——只是普通vector<int>
的编码。当然,这里没有显示与此代码相关的大量内容,但您可能可以了解基本概念。
另一个注意事项,如果您正在执行 GPU 计算,或者如果您有一个特殊的问题,例如 64 位是合适的大小,那么将所有内容都塞进原生单词中可能是值得的。否则,我猜您添加到搜索过程中的开销可能会压倒您通过更快的编码和解码获得的任何好处。
编辑::我已经决定我对此有点傻。虽然你最终得到一个双精度,但它假设位集包含一个整数......这是一个很大的假设。您最终会得到每个位集的可预测和可重复的值,但我仍然认为这不是作者的意图。
好吧,如果您遍历位值并执行
output_double += pow( 2, 64-(bit_position+1) ) * bit_value;
那会奏效。只要是大端