我有一个Java程序,用ProcessBuilder
启动C ++程序,并以字符串形式读取C ++应用程序的输出。数据是数据类型的混合,我通过std::cout
将所有内容序列化为字符串,如下所示:
void write_data(int x) {
std::cout << "int:" << x << std::endl;
}
void write_data(double x) {
std::cout << "double:" << x << std::endl;
}
在Java方面,我做了类似的事情:
String line = readLineFromSubProcess();
String[] parts = line.split(":");
if (parts[0].equals("double")) {
return Double.parseDouble(parts[1]);
}
然而,由于我正在读取传感器数据,因此我担心浮点值的精度损失。我可以直接控制序列化格式。
如何从C ++写入double
值到std::cout
的方式,从Java读取它会产生完全相同的double值?
答案 0 :(得分:2)
我决定采用的解决方案是reinterpret_cast
double
至int64_t
和float
至int32_t
,以及仅对这两种数据类型使用部分模板专门化。我仍然错过了endianess转换,但由于这里的所有内容都是小端,我想我会没事的:
template <typename T>
void write_data(T x) {
std::cout << get_type_name<T>() << ":" << x << std::endl;
}
template<>
void write_data<double>(double x) {
union {
double d;
int64_t i;
} n;
n.d = x;
std::cout << "double_as_int64:" << n.i << std::endl;
}
template<>
void write_data<float>(double x) {
union {
float f;
int32_t i;
} n;
n.f = x;
std::cout << "float_as_int32:" << n.i << std::endl;
}
由于我的编译器(GCC)给了我一个warning about aliasing,我不会真正做reinterpret_cast
,这与我们在这里使用的编译器开关有关:
取消引用类型惩罚指针将破坏严格别名规则
在Java方面,我正在做:
String line = readLineFromSubProcess();
String[] parts = line.split(":");
if (parts[0].equals("double")) {
return Double.parseDouble(parts[1]);
} else if (parts[0].equals("double_as_int64") {
return Double.longBitsToDouble(Long.parseLong(parts[1]));
} else if (parts[0].equals("float_as_int32") {
return Float.intBitsToFloat(Integer.parseInt(parts[1]));
}
答案 1 :(得分:1)
好吧,正如你所说,如果你关心可读性并需要打印双打,有两个选择:
方法1: (可读)
#include <limits>
void double_to_string(double the_double, int offset_to_max)
{
typedef std::numeric_limits<double> doublelimit;
// ready to transfer
std::cout.precision(doublelimit::digits10 + offset_to_max);
std::cout << the_double << std::endl;
}
// Back to double
double cstring_to_double(const char *s)
{
return std::atof(s);
}
int main(int argc, char *argv[]) {
double the_double = 0.10000000000000002;
//double the_double = 0.1;
int offset = 2;
double_to_string(the_double, offset);
}
这样,打印的双打将使用全精度(17)进行混叠。我建议使用17,因为默认将始终尝试舍入。无论哪种方式,使用此方法的完整正确打印将需要更强大的库。
方法2: (二进制)
只需memcpy值,使用和int64就可以了,double可以存储最多53位:
template<typename T, typename H = int64_t>
H write_data(T x)
{
H i;
// Can we hold the data?
if (sizeof(T) > sizeof(H)) assert(false); // or write some error procesing
// Now take the lower size of the two.
memcpy(&i, &x, sizeof(H) > sizeof(T) ? sizeof(T) : sizeof(H));
return i;
}
// union version
template<typename T, typename H = int64_t>
H write_data_u(T x)
{
// Can we hold the data?
if (sizeof(T) > sizeof(H)) assert(false); // or write some error procesing
union {
T d;
H i;
} n;
n.d = x;
return n.i;
}
int main(int argc, char *argv[])
{
double the_double = 0.10000000000000001;
int64_t double_holder = write_data<double, int64_t>(the_double);
// Binary data as decimal string: ready to transfer the string, remember to convert to big_endian first if you transfer the binary representation.
std::cout << "double_as_int64:" << double_holder << std::endl;
// Back to double (we know int64_t holds a double so we dont care about the remaining bits)
double back_to_double = write_data<int64_t, double>(double_holder);
std::cout.precision(std::numeric_limits<double>::digits10 + 2);
std::cout << "back_to_double:" << back_to_double << std::endl;
}
答案 2 :(得分:-1)
这会有帮助吗?
std::cout << std::setprecision(5) << f << '\n';
请参阅以下示例: