4
#include <iostream>
#include <string>
#include <limits>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

int main()
{
    std::cout.precision(std::numeric_limits<double>::max_digits10);

    // create a JSON value with different types
    json json_types =
    {
        {"number_b", 1234567890.0123456789}
    };

    std::cout << std::fixed << json_types.dump(4) << std::endl;

    auto v2 = json_types["number_b"].get<double>();
    std::cout << "number_b in double: " << std::fixed << v2 << std::endl;

    // auto v3 = json_types["number_b"].get<std::string>();  
    // [json.exception.type_error.302] type must be string, but is number

}

Output:

{
    "number_b": 1234567890.0123458
}
number_b in double: 1234567890.01234579086303711

Is there a way that I can get the value of number_b as a std::string based on the original input value of "1234567890.0123456789"?

Note: the JSON number value is passed in through a third-party and I cannot change the input value from a number to a string from the source.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
q0987
  • 34,938
  • 69
  • 242
  • 387
  • 1
    [`std::to_string(double)`](https://en.cppreference.com/w/cpp/string/basic_string/to_string)? – JohnFilleau Apr 08 '22 at 15:06
  • The issue is that the converted double from the library already lost double precision if the number is very large. – q0987 Apr 08 '22 at 15:13
  • 1
    `double` [is not exact](https://en.cppreference.com/w/cpp/types/numeric_limits/is_exact). Despite the _representation_ `1234567890.0123456789` appearing in your code, that value is not necessarily what is in your json structure. – Drew Dormann Apr 08 '22 at 15:17
  • 1
    @JohnFilleau, the output results show you the dumped string already. – q0987 Apr 08 '22 at 15:34
  • @JohnFilleau, the vendor sends the data in raw string as json format and we convert the raw string into json object through nlohmann::json library. Other vendors send the data in string fields for all numeric values so we didn't have problems. – q0987 Apr 08 '22 at 15:51

3 Answers3

1

A double has about 15-17 decimal places of precision [1]. A double cannot store 1234567890.0123456789 exactly and thus you will not be able to convert it to the exact string, if it is stored into a double first.

You can try to hook on the sax interface [2], that the nlohmann library provides (more specifically the number_float()) function to get the original string, that was present in the json during parsing.

I would however not rely on such hacks. Working with floating point numbers is inherently not exact and it is probably better to just look if the number is in a small range or something similar. This however depends on the exact problem that your are trying to solve.

Jakob Stark
  • 3,346
  • 6
  • 22
  • Long story short. we don't store those numbers as double instead we use __int128_t. However, in this case, this vendor doesn't send the number through string instead they just send out a decimal number in json format and this is the issue we have. – q0987 Apr 08 '22 at 15:36
  • @q0987 so you want to convert a `double` into a `__int128_t`? I'm afraid I do not fully understand your problem then. Why does the fractional part matter, if you are going to store it into an integer anyway? – Jakob Stark Apr 08 '22 at 15:41
  • The issue is that we need to get a value from the json field which is parsed through nlohmann::json.hpp library. We cannot get the string value representation directly from the json field b/c it will coredump. Also we will lost precision if we get the numeric value from get. – q0987 Apr 08 '22 at 15:45
  • @q0987 but if you store the value into an `__int128_t` you are losing all sub-integer precision. Anyway the sax interface I linked provides a way to access the string value representation from the json field directly. – Jakob Stark Apr 08 '22 at 15:54
  • we will convert those double into __int128_t so we keep all precision. For example, double a = 1.234 => 1234 etc. – q0987 Apr 08 '22 at 15:58
  • It sounds like making your own sax interface as described in link [2] and using that to parse is the way to go. Note that you'll be stealing code from the `detail` directory, so it's subject to change. I believe you only need to reimplement the `number_float` function. – JohnFilleau Apr 08 '22 at 16:10
  • It is a pity, that the nlohmann library provides no better interface for sax parsing. For example rapidjson has a much better support for that kind of stuff. There you can write sax [*filters*](https://rapidjson.org/md_doc_sax.html#Filtering) to change the json during parsing. – Jakob Stark Apr 08 '22 at 17:17
0

It's impossible.

  1. The example above lost precision on the compilation time.
  2. RFC7159. Technically you should not receive such JSON as an input source since software that sends you such JSON Number is usually conforming IEEE 754-2008.

A JSON number such as 1E400 or 3.141592653589793238462643383279 may indicate potential interoperability problems, since it suggests that the software that created it expects receiving software to have greater capabilities for numeric magnitude and precision than is widely available.

273K
  • 29,503
  • 10
  • 41
  • 64
0

For now there is no easy way.

You need implement your own SAX parser.

See:


If you consider switch to other JSON lib that allows such operation is RAPID Json.

const auto& document = parser.Parse<rapidjson::kParseNumbersAsStringsFlag>(data.data(), data.length());
Gelldur
  • 11,187
  • 7
  • 57
  • 68