c++ - Jsoncpp 错误地写入浮点值

标签 c++ json floating-point jsoncpp

我正在使用 jsoncpp 读取 JSON 文件。当我写回文件时,我的浮点值略有偏差。为了进行测试,我决定将文件解析为 Json::Value,然后将该值写回到文件中。我希望它看起来一样,但浮点值不同。

例子:

"Parameters":
{
"MinXValue": 0.1,
"MaxXValue": 0.15,
"MinYValue": 0.25,
"MaxYValue": 1.1,
"MinObjectSizeValue": 1
}

写成:

"Parameters":
{
"MinXValue": 0.10000000000000001,
"MaxXValue": 0.14999999999999999,
"MinYValue": 0.25,
"MaxYValue": 1.1000000238418579,
"MinObjectSizeValue": 1
}

您可能会注意到 0.25 没有变化,尽管所有其他 float 都发生了变化。知道这里发生了什么吗?

最佳答案

其实是 float 解析/打印实现的问题。虽然 float 只能精确表示一些十进制数(0.25 是 ~2^64 之一),但需要将字符串表示解析为最接近的二进制表示。打印 float 时,还需要打印(最好是最短的)可以还原为二进制表示的字符串表示。

我承认我没有调查 JsonCPP 看是否有解决方案。但因为我是 RapidJSON 的作者,我试着看看 RapidJSON 如何执行此操作:

const char json[] = 
    "{"
    "\"MinXValue\": 0.1,"
    "\"MaxXValue\": 0.15,"
    "\"MinYValue\": 0.25,"
    "\"MaxYValue\": 1.1,"
    "\"MinObjectSizeValue\": 1"
    "}";

using namespace rapidjson;

Document d;
d.Parse(json);

StringBuffer sb;
PrettyWriter<StringBuffer> writer(sb);
d.Accept(writer);

std::cout << sb.GetString();

结果:

{
    "MinXValue": 0.1,
    "MaxXValue": 0.15,
    "MinYValue": 0.25,
    "MaxYValue": 1.1,
    "MinObjectSizeValue": 1
}

RapidJSON 在内部实现了解析和打印算法。普通精度解析将有最多 3 个 ULP 错误,但是使用全精度解析标志 (kParseFullPrecisionFlag) 它总是可以解析到最接近的表示。打印部分实现了Grisu2算法。它确实总是会生成准确的结果,并且超过 99% 的时间是最短的(最优的)。

其实用strtod()sprintf(..., "%.17g", ...)也可以解决这个问题。但它们在当前的 C/C++ 标准库中要慢得多。比如我做了一个benchmark用于打印 double。因此,在 RapidJSON 中,我们实现了自己优化的仅 header 解决方案。

关于c++ - Jsoncpp 错误地写入浮点值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29832984/

相关文章:

java - 四舍五入到小数点后两位

c++ - boost::asio::buffer:获取缓冲区大小并防止缓冲区溢出?

c++ - 新动态字符数组末尾的空字符

jquery - ASP.Net MVC 中的临时数据与 JSON 请求

安卓 JSON 对象

assembly - 从使用x87 FPU的64位汇编函数返回 float

C++成员变量初始化顺序

c++ - 自动提升 LLVM 指令的输入值

javascript - 如何将不同大小的数组转换为 JSON 对象?

c++ - 将 mpfr_t(或任何其他任意精度库类型)转换为 __float128