C++中的 Json:将数字解析为字符串以避免浮点不准确

Json in C++: Parse a number as a string to avoid floating-point inaccuracy

本文关键字:字符串 不准确 Json 中的 数字 C++      更新时间:2023-10-16

>我正在处理加密货币RPC并接收json数据,如下所示:

{
...
"amount": 1.34000000,
"confirmations": 230016,
"spendable": true,
"solvable": true
...
}

使用 jsoncpp 库或 json11 获取解析为double的数字。发生这种情况时,结果是:1.3400000000000001,由于双重精度问题。总的来说,这对金融转移来说是灾难性的,是不可接受的。

我已经有一个定点库,它可以接受一个有效的字符串并在内部将其视为整数。有没有办法让 Jsoncpp(或任何其他 json 库(将选定的数字 json 值作为字符串,以便我可以以固定精度以正确的方式处理它们?

json 库中似乎没有解决方案,所以我不得不自己修改数字并用引号括起来。我将此功能应用于响应以执行此操作。

[](std::string& jsonStr) {
// matches "amount" field in json
static std::regex reg(R"((s*"amount"s*:)s*(d*.{0,1}d{0,8})s*)");
jsonStr = std::regex_replace(jsonStr, reg, "$1"$2"");
};

现在它工作正常。

我喜欢ThorsSerializer。免责声明是我写的。

它支持您正在寻找的内容。
您可以告诉解析器对类使用标准输入/输出运算符(然后您可以自己定义(。

例:

#include "ThorSerialize/JsonThor.h"
#include "ThorSerialize/SerUtil.h"
#include <sstream>
#include <iostream>
#include <string>
#include <map>
struct FixedPoint
{
int     integerPart;
int     floatPart;
friend std::istream& operator>>(std::istream& stream, FixedPoint& data)
{
// This code assumes <number>.<number>
// Change to suite your needs.
char c;
stream >> data.integerPart >> c >> data.floatPart;
if (c != '.')
{
stream.setstate(std::ios::failbit);
}
return stream;
}
};
// This declaration tells the serializer to use operator>> for reading
// and operator<< for writing this value.
// Note: The value must still conform to standard Json type
//       true/false/null/integer/real/quoted string
ThorsAnvil_MakeTraitCustom(FixedPoint);
struct BitCoin
{
FixedPoint  amount;
int         confirmations;
bool        spendable;
bool        solvable;
};
// This declaration tells the serializer to use the standard
// built in operators for a struct and serialize the listed members.
// There are built in operations for all built in types and std::Types
ThorsAnvil_MakeTrait(BitCoin, amount, confirmations, spendable, solvable);

用法示例:

int main()
{
using ThorsAnvil::Serialize::jsonImport;
using ThorsAnvil::Serialize::jsonExport;
std::stringstream file(R"(
{
"amount": 1.34000000,
"confirmations": 230016,
"spendable": true,
"solvable": true
}
)");
BitCoin     coin;
file >> jsonImport(coin);
std::cout << coin.amount.integerPart << " . " << coin.amount.floatPart << "n";
}

建:

> g++ -std=c++1z 51087868.cpp -lThorSerialize17

原生的 jsoncpp 解决方案是 RTFM!!(例如,此处:https://open-source-parsers.github.io/jsoncpp-docs/doxygen/class_json_1_1_stream_writer_builder.html(

Json::StreamWriterBuilder builder;
builder["commentStyle"] = "None";
builder["indentation"] = "   ";
builder["precision"] = 15;

这将设置编写器浮点精度,以避免在双精度表示中打印小截断错误。 例如,代替 JSON 字段,

"金额": 1.3400000000000001,

你现在将得到

"金额": 1.340000000000000

如愿以偿。