我有一个IEEE754双精度64位二进制字符串表示的双精度数。
例如:double value = 0.999;它的二进制表示为"0011111111101111111101111100111011011001000101101000011100101011"
我想把这个字符串转换回C++中的双精度数,不想使用任何外部库或.dll文件,因为我的程序将在任何平台上运行。
我有一个IEEE754双精度64位二进制字符串表示的双精度数。
例如:double value = 0.999;它的二进制表示为"0011111111101111111101111100111011011001000101101000011100101011"
我想把这个字符串转换回C++中的双精度数,不想使用任何外部库或.dll文件,因为我的程序将在任何平台上运行。
C 字符串解决方案:
#include <cstring> // needed for all three solutions because of memcpy
double bitstring_to_double(const char* p)
{
unsigned long long x = 0;
for (; *p; ++p)
{
x = (x << 1) + (*p - '0');
}
double d;
memcpy(&d, &x, 8);
return d;
}
std::string
的解决方案:
#include <string>
double bitstring_to_double(const std::string& s)
{
unsigned long long x = 0;
for (std::string::const_iterator it = s.begin(); it != s.end(); ++it)
{
x = (x << 1) + (*it - '0');
}
double d;
memcpy(&d, &x, 8);
return d;
}
通用解决方案:
template<typename InputIterator>
double bitstring_to_double(InputIterator begin, InputIterator end)
{
unsigned long long x = 0;
for (; begin != end; ++begin)
{
x = (x << 1) + (*begin - '0');
}
double d;
memcpy(&d, &x, 8);
return d;
}
示例调用:
#include <iostream>
int main()
{
const char * p = "0011111111101111111101111100111011011001000101101000011100101011";
std::cout << bitstring_to_double(p) << '\n';
std::string s(p);
std::cout << bitstring_to_double(s) << '\n';
std::cout << bitstring_to_double(s.begin(), s.end()) << '\n';
std::cout << bitstring_to_double(p + 0, p + 64) << '\n';
}
注意:我假定 unsigned long long
有64位。更清晰的解决方案是包含 <cstdint>
并使用 uint64_t
,前提是您的编译器保持最新,并提供该 C++11 标头。
return *reinterpret_cast<double*>(&x);
而不是memcpy()呢?这似乎是reinterpret_cast实际有意义的少数情况之一。 - Adam Zalcmanunion { uint64_t src; double dst; } c; while (*s) { c.src = (c.src << 1) + (*s++ - '0'); } return c.dst;
- onitakememcpy
操作与别名一样,不再是“未定义行为”。 - Hot Licksc_str()
应该可以正常工作,但是我还是添加了一个 std::string
的解决方案,以防万一。而且这是 C++,我们毕竟要考虑通用性 ;) - fredoverflowdouble
的单个位。这是一个二进制位的字符字符串吗?如果是,首先将其转换为64位整数。然后可以使用库例程(可能某处有一个),或者更简单地使用double
别名覆盖在64位整数上进行转换为double
。
(如果它已经是一个64位整数,则跳过第一步。)
忽略字节顺序问题,但我认为这应该是一个可行的选项:
在i386上使用gcc,下面的代码结果为.999。可以在此链接中查看实际效果:https://ideone.com/i4ygJ
#include <cstdint>
#include <sstream>
#include <iostream>
#include <bitset>
int main()
{
std::istringstream iss("0011111111101111111101111100111011011001000101101000011100101011");
std::bitset<32> hi, lo;
if (iss >> hi >> lo)
{
struct { uint32_t lo, hi; } words = { lo.to_ulong(), hi.to_ulong() };
double converted = *reinterpret_cast<double*>(&words);
std::cout << hi << std::endl;
std::cout << lo << std::endl;
std::cout << converted << std::endl;
}
}
#include <math.h>
...
int const dbl_exponent_bits = 11;
int const dbl_exponent_offset = 1023;
int const dbl_significand_bits = 52;
bool negative = (*num++ == '1');
int exponent = 0;
for (int i = 0; i < dbl_exponent_bits; ++i, ++num) {
exponent = 2*exponent + (*num == '1' ? 1 : 0);
}
double significand = 1;
for (int i = 0; i < dbl_significand_bits; ++i, ++num) {
significand = 2*significand + (*num == '1' ? 1 : 0);
}
assert(*num == '\0');
double result = ldexp(significand, exponent-(dbl_exponent_offset+dbl_significand_bits));
if (negative)
result = -result;
int
与double
的大小匹配。(我不是“标准律师”,也不知道这是否属实。)如果不是,那么这项工作就会变得更加困难。 - Hot Licks