Jsoncpp错误地写入浮点值

时间:2015-04-23 19:48:42

标签: 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没有改变,即使所有其他花车也没有改变。知道这里发生了什么吗?

3 个答案:

答案 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()