我正在使用 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/