如何实现double的序列化和反序列化?

时间:2014-11-26 12:27:43

标签: c++ qt gcc serialization floating-point

我正在尝试解决相对简单的问题,即能够将双重写入文件,然后再再次将文件读入双重。基于this answer,我决定使用人类可读的格式。

根据this question,我成功地避免了一些编译器遇到的问题 nan [ - ] infinity 。对于有限数字,我使用std::stod函数将数字的字符串表示转换为数字本身。但有时解析会因数字接近于零而失败,例如在以下示例中:

#include <cmath>
#include <iostream>
#include <sstream>
#include <limits>

const std::size_t maxPrecision = std::numeric_limits<double>::digits;
const double small = std::exp(-730.0);

int main()
{
    std::stringstream stream;
    stream.precision(maxPrecision);
    stream << small;
    std::cout << "serialized:    " << stream.str() << std::endl;
    double out = std::stod(stream.str());
    std::cout << "de-serialized: " << out << std::endl;
    return 0;
}

在我的机器上,结果是:

serialized:     9.2263152681638151025201733115952403273156653201666065e-318
terminate called after throwing an instance of 'std::out_of_range'
  what():  stod
The program has unexpectedly finished.

也就是说,数字太接近零而无法正确解析。起初我认为问题是这个数字是denormal,但事实并非如此,因为尾数以9而不是0开头。

另一方面,Qt对这个号码没有任何问题:

#include <cmath>
#include <limits>

#include <QString>
#include <QTextStream>

const std::size_t maxPrecision = std::numeric_limits<double>::digits;
const double small = std::exp(-730.0);

int main()
{
    QString string = QString::number(small, 'g', maxPrecision);
    QTextStream stream(stdout);
    stream.setRealNumberPrecision(maxPrecision);
    stream << "serialized:    " << string << '\n';
    bool ok;
    double out = string.toDouble(&ok);
    stream <<  "de-serialized: " << out << '\n' << (ok?"ok":"not ok") << '\n';
    return 0;
}

输出:

serialized:    9.2263152681638151025201733115952403273156653201666065e-318
de-serialized: 9.2263152681638151025201733115952403273156653201666065e-318
ok

要点:

  1. 这是标准库的gcc实现中的错误吗?
  2. 我可以优雅地规避这个吗?
  3. 我应该只使用Qt吗?

2 个答案:

答案 0 :(得分:5)

回答问题#2:

这可能是我的“C-way”思维方式,但您可以将double复制到uint64_t(mem-copying,而不是类型转换),序列化{{1}相反,在反序列化时做相反的事情。

以下是一个示例(甚至无需从uint64_t复制到double,反之亦然):

uint64_t

请注意,为了避免中断strict-aliasing rule,您实际上需要先复制它,因为标准不会将uint64_t* pi = (uint64_t*)&small; stringstream stream; stream.precision(maxPrecision); stream << *pi; cout << "serialized: " << stream.str() << endl; uint64_t out = stoull(stream.str()); double* pf = (double*)&out; cout << "de-serialized: " << *pf << endl; double分配到同一地址-alignment:

uint64_t

答案 1 :(得分:1)

如果您对其他录制方法持开放态度,可以使用frexp

#include <cmath>
#include <iostream>
#include <sstream>
#include <limits>


const std::size_t maxPrecision = std::numeric_limits<double>::digits;
const double small = std::exp(-730.0);

int main()
{
    std::stringstream stream;
    stream.precision(maxPrecision);

    int exp;
    double x = frexp(small, &exp);

    //std::cout << x << " * 2 ^ " << exp << std::endl;
    stream << x << " * 2 ^ " << exp;

    int outexp;
    double outx;

    stream.seekg(0);

    stream >> outx;
    stream.ignore(7); // >> " * 2 ^ "
    stream >> outexp;

    //std::cout << outx << " * 2 ^ " << outexp << std::endl;

    std::cout << small << std::endl << outx * pow(2, outexp) << std::endl;

    return 0;
}
相关问题