artyom-beilis/cppcms

Reading large numbers from the JSON causes a bad_cast exception even when the target datatype can store the number

Closed this issue · 1 comments

Hi,
When storing large numbers in a JSON (for example INT64_MAX) a bad_cast error gets thrown instead of returning the value even when the target data type can store the value.

#include <iostream>
#include <sstream>
#include <string>

#include <cppcms/json.h>

std::string const small_number_json = {
R"({
  "test": 51245,
})"
};

const std::string small_number_as_string = "51245";

std::string const too_large_number_json = {
R"({
  "test": 9223372036854775807,
})"
};

const std::string too_large_number_as_string = "9223372036854775807";

void test_json_and_string(const std::string& json, const std::string& number)
{
  cppcms::json::value my_root;
  long long  my_number_as_longlong;
  std::istringstream my_root_stream{json};
  std::istringstream my_number_stream{number};

  my_root_stream >> my_root;
  my_number_stream >> my_number_as_longlong;

  std::cout << my_number_as_longlong << std::endl;
  std::cout << my_root.get<long long>("test") << std::endl;
}

int main(void)
{
  test_json_and_string(small_number_json, small_number_as_string); // OK
  test_json_and_string(too_large_number_json, too_large_number_as_string); // FAIL

  return 0;
}

The same happens when using int64_t.

Thanks.

This isn't a bug this is actually right behavior.

JSON and JavaScript in general requires numbers to be represented as at least double and this is what cppcms::json does - it keeps values as double.

INT64_MAX can't be represented exactly in double values that has mantissa of 53 bits and thus can't represnet 64 bit integer correctly.

Now take a look on the sample:

    #include <iterator>
    #include <iostream>
    int main()
    {
            double v=9223372036854775807.0;
            std::cout << std::fixed;
            std::cout << "Double    " << v << std::endl;
            std::cout << "Int64 MAX " << INT64_MAX << std::endl;
            long long iv = static_cast<long long>(v);
            std::cout << "Casted    " << iv << std::endl;
            std::cout << "Int=doub  " << (v==iv) << std::endl;
    }

Casting to long long will lead to negative number which is clearly wrong. this the behavior is correct