SFB*_*A26 7 c++ floating-point json jsoncpp
我正在使用jsoncpp从JSON文件中读取.当我写回文件时,我的浮点值略有偏差.为了测试,我决定将文件解析为Json :: Value,然后将该值写回文件.我希望它看起来一样,但浮动值不同.
例:
"Parameters":
{
"MinXValue": 0.1,
"MaxXValue": 0.15,
"MinYValue": 0.25,
"MaxYValue": 1.1,
"MinObjectSizeValue": 1
}
Run Code Online (Sandbox Code Playgroud)
写道:
"Parameters":
{
"MinXValue": 0.10000000000000001,
"MaxXValue": 0.14999999999999999,
"MinYValue": 0.25,
"MaxYValue": 1.1000000238418579,
"MinObjectSizeValue": 1
}
Run Code Online (Sandbox Code Playgroud)
您可能会注意到0.25没有改变,即使所有其他浮动都没有改变.知道这里发生了什么吗?
这实际上是浮点数解析/打印实现的问题。虽然浮点数只能精确地表示一些十进制数(0.25 是 ~2^64 之一),但有必要将字符串表示解析为最接近的二进制表示。打印浮点数时,还需要打印可以恢复为二进制表示的(最好是最短的)字符串表示。
我承认我没有调查 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();
Run Code Online (Sandbox Code Playgroud)
结果:
{
"MinXValue": 0.1,
"MaxXValue": 0.15,
"MinYValue": 0.25,
"MaxYValue": 1.1,
"MinObjectSizeValue": 1
}
Run Code Online (Sandbox Code Playgroud)
RapidJSON 在内部实现了解析和打印算法。正常精度解析最多有 3 个 ULP 错误,但使用全精度解析标志 ( kParseFullPrecisionFlag
),它始终可以解析到最接近的表示形式。打印部分实现了Grisu2算法。它总是生成准确的结果,并且 99% 以上的时间都是最短(最佳)的。
实际上,使用strtod()
andsprintf(..., "%.17g", ...)
也可以解决这个问题。但它们在当前的 C/C++ 标准库中要慢得多。例如,我做了一个打印的基准测试double
。因此,在 RapidJSON 中,我们实现了自己优化的仅标头解决方案。
对于那些仍在研究此问题的人来说,此功能已得到支持:https://github.com/open-source-parsers/jsoncpp/commit/772f634548f0cec058a0f16a2e641d9f7b78343d
std::ofstream ofs("example.json");
Json::Value root;
// ... Build your json object....
Json::StreamWriterBuilder wbuilder;
wbuilder["indentation"] = "";
wbuilder.settings_["precision"] = 6;
std::unique_ptr<Json::StreamWriter> writer(wbuilder.newStreamWriter());
// Write to file.
writer->write(root, &ofs);
ofs.close();
Run Code Online (Sandbox Code Playgroud)