Json в C ++: анализировать число как строку, чтобы избежать неточностей с плавающей точкой - PullRequest
0 голосов
/ 28 июня 2018

Я имею дело с криптовалютным RPC и получаю данные json следующим образом:

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

Используя библиотеку Jsoncpp или json11 , получают число, проанализированное для double. Когда это происходит, результат: 1.3400000000000001 из-за проблем с двойной точностью. В целом это катастрофично для финансовых транзакций и неприемлемо.

У меня уже есть библиотека с фиксированной запятой, которая может принимать допустимую строку и внутренне рассматривать ее как целое число. Есть ли способ, которым я мог бы заставить Jsoncpp (или любую другую библиотеку json) принимать выбранные числовые значения json в виде строк, чтобы я мог правильно обращаться с ними с фиксированной точностью?

Ответы [ 3 ]

0 голосов
/ 11 июля 2018

Мне нравится 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
0 голосов
/ 11 марта 2019

Собственное решение 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,

по желанию.

0 голосов
/ 28 июня 2018

Похоже, что в библиотеках 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\"");
    };

А теперь работает нормально.

...