我正在使用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没有改变,即使所有其他花车也没有改变。知道这里发生了什么吗?
答案 0 :(得分:3)
实际上是浮点数解析/打印实现的问题。虽然浮点数只能精确地表示一些十进制数(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();
结果:
{
"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中,我们实现了自己优化的仅限标头的解决方案。
答案 1 :(得分:2)
一种解决方案是对jsoncpp源文件进行少量更改。
在下面的行中将17替换为15,以使其读取(我的副本中的第4135行):
std::string valueToString(double value) { return valueToString(value, false, 15); }
基本上,这会将最大印刷数字位数从17减少到15,但是如果您认为可以,那么似乎可以解决您提到的所有不良印刷伪像。我认为有人可能还会争辩说您无论如何都不应使用json来传递> 15个有效数字(接近双精度的限制),但这是另一回事了……
例如以前为我打印的内容是:
"yo" : 1726.6969999999999,
现在打印为:
"yo" : 1726.697,
答案 2 :(得分:1)
对于仍在研究此问题的用户,确实支持此功能:https://github.com/open-source-parsers/jsoncpp/commit/772f634548f0cec058a0f16a2e641d9f7b78343d
.Replace()