可以看到图中71.21的值经过jsoncpp转换后变成了71.20999999999999
这是因为 21/100 没有精确的浮点表示,默认情况下,我们使用17位数字,因为这是将double的任何值明确表示为字符串所需的数字。此17==DBL_DECIMAL_DIG==std::numeric_limits<double>::max_digist10。以这种精度打印可以保证任何双值都可以在打印和解析时存活下来,而不会失真。这与15==DBL_DIG==std::numeric_limits<double>::digitals10不同,后者是可以在不失真的情况下从字符串解析到double并返回的最大位数。因此,17的默认值是一种折衷。我们选择了足够多的数字来完美地表示每一个可能的双精度值。不过,这不是我想要的。所以只需要15位数字的精度,因为15位数字可以在不失真的情况下解析和重新打印,但这会牺牲对double数据类型的完整尾数的访问。
如果您需要自定义输出并使用自定义配置的编写器,有一种方法可以设置精度。
文件:jsoncpp/src/lib_json/json_writer.cpp
Line 1240
(*settings)["precision"] = 17;
将17修改为15即可正常输出71.21
以下代码可以看出计算机对不同位数精度的不同输出:
#include <iostream>
#include <iomanip>
#include <string>
#include <cmath>
constexpr double inf = std::numeric_limits<double>::infinity();
void dumpTable(double x) {
int maxPrecision = 25;
double xPlusEpsilon = std::nextafter(x, inf);
for (int p = 0; p < maxPrecision; ++p) {
std::cout << "p=" << std::setw(3) << p;
for (auto f : {x, xPlusEpsilon}) {
std::cout
<< ", "
<< std::setprecision(p)
<< std::defaultfloat
<< std::setw(maxPrecision + 5)
<< f;
}
std::cout << std::endl;
}
}
int main() {
dumpTable(71.21);
return 0;
}
可以从下面看出16位时,输出71.21则会出错,当然不止72.21,一些其它浮点类型也可能会出错。
p= 0, 7e+01, 7e+01
p= 1, 7e+01, 7e+01
p= 2, 71, 71
p= 3, 71.2, 71.2
p= 4, 71.21, 71.21
p= 5, 71.21, 71.21
p= 6, 71.21, 71.21
p= 7, 71.21, 71.21
p= 8, 71.21, 71.21
p= 9, 71.21, 71.21
p= 10, 71.21, 71.21
p= 11, 71.21, 71.21
p= 12, 71.21, 71.21
p= 13, 71.21, 71.21
p= 14, 71.21, 71.21
p= 15, 71.21, 71.21
p= 16, 71.20999999999999, 71.21000000000001
p= 17, 71.209999999999994, 71.210000000000008
p= 18, 71.2099999999999937, 71.210000000000008
p= 19, 71.20999999999999375, 71.21000000000000796
p= 20, 71.209999999999993747, 71.210000000000007958
p= 21, 71.2099999999999937472, 71.2100000000000079581
p= 22, 71.20999999999999374722, 71.21000000000000795808
p= 23, 71.209999999999993747224, 71.210000000000007958079
p= 24, 71.2099999999999937472239, 71.2100000000000079580786