繁体   English   中英

什么是将double序列化为字符串并再次返回的最佳方法?

[英]What's the best way to serialize a double to a string and back again?

我有一个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值?

我决定使用的解决方案是将double reinterpret_castint64_t ,将float reinterpret_castint32_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)给了我一个关于别名警告 ,我不是字面意思做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]));
}

这就是Google Protocol Buffers正在做的事情

好吧,正如你所说,有两种选择,如果你关心可读性并需要打印双打,那么:

方法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;
}

这会有帮助吗?

std::cout << std::setprecision(5) << f << '\n';

请参阅以下示例:

http://www.cplusplus.com/reference/iomanip/setprecision/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM