-
-
Notifications
You must be signed in to change notification settings - Fork 6.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bugs in miloyip/nativejson-benchmark: floating-point parsing #186
Comments
These are the failing test cases mentioned above: TEST_DOUBLE("[-0.0]", -0.0);
TEST_DOUBLE("[2.22507385850720113605740979670913197593481954635164564e-308]", 2.2250738585072009e-308);
TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984374]", 0.99999999999999989); // previous double
TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203126]", 1.00000000000000022); // next double
TEST_DOUBLE("[7205759403792793199999e-5]", 72057594037927928.0);
TEST_DOUBLE("[922337203685477529599999e-5]", 9223372036854774784.0);
TEST_DOUBLE("[1014120480182583464902367222169599999e-5]", 10141204801825834086073718800384.0);
TEST_DOUBLE("[5708990770823839207320493820740630171355185151999e-3]", 5708990770823838890407843763683279797179383808.0); |
With the following program, I can reproduce all but the first error: #include <json.hpp>
using nlohmann::json;
int main()
{
json j02 = json::parse("-0.0");
json j37 = json::parse("2.22507385850720113605740979670913197593481954635164564e-308");
json j40 = json::parse("0.999999999999999944488848768742172978818416595458984374");
json j44 = json::parse("1.00000000000000011102230246251565404236316680908203126");
json j48 = json::parse("7205759403792793199999e-5");
json j53 = json::parse("922337203685477529599999e-5");
json j58 = json::parse("1014120480182583464902367222169599999e-5");
json j63 = json::parse("5708990770823839207320493820740630171355185151999e-3");
std::cout << std::boolalpha
<< (j02.get<double>() == -0.0) << '\n'
<< (j37.get<double>() == 2.2250738585072009e-308) << '\n'
<< (j40.get<double>() == 0.99999999999999989) << '\n'
<< (j44.get<double>() == 1.00000000000000022) << '\n'
<< (j48.get<double>() == 72057594037927928.0) << '\n'
<< (j53.get<double>() == 9223372036854774784.0) << '\n'
<< (j58.get<double>() == 10141204801825834086073718800384.0) << '\n'
<< (j63.get<double>() == 5708990770823838890407843763683279797179383808.0) << '\n';
} Output:
|
Interestingly, the exact same benchmark is already part of the unit tests (test case "compliance tests from nativejson-benchmark", section "doubles"). There the following comparison code is used: auto TEST_DOUBLE = [](const std::string & json_string, const double expected)
{
CAPTURE(json_string);
CAPTURE(expected);
CHECK(json::parse(json_string)[0].get<double>() == Approx(expected));
}; |
I am confused :-) |
The tests all pass on VS2015 with the current code but fail on GCC. The reason for this is that VS2015 treats The obvious solution is to parse under GCC using The solution that works in all cases is to wrap |
Where does get_number() in 1.0.1 cast to a double? Do you mean that the test casts to a double by calling get<double>() instead of get<long double>()?
|
Nope, later in the precision check in ...
// we would lose precision -> return float
result.m_type = value_t::number_float;
result.m_value = static_cast<number_float_t>(float_val);
... In the default The way I put it was probably unclear. What I mean was that |
@nlohmann, the reason why The benchmark compares them as if they are uint64_t via a union, so it is looking for equality of the bit pattern. An example that illustrates the difference is: union double_union {
double _double;
uint64_t _uint64_t;
};
int main()
{
double_union pos_zero = { 0.0 };
double_union neg_zero = { -0.0 };
if(pos_zero._double == neg_zero._double)
std::cout << "0.0 == -0.0 when tested as a double" << std::endl;
if(pos_zero._uint64_t == neg_zero._uint64_t)
std::cout << "0.0 == -0.0 when tested as a uint64_t" << std::endl; // Never reached
return 0;
} Which produces the output:
As I mention in #187 the only fix for this is to keep -0.0 as a floating point number. I believe we should be doing this because although 0.0 == -0.0 there are differences between the two in some contexts and it can be mathematically convenient to have a negative zero (which is why it is in IEEE 754 to begin with). |
Note that to make the round trip for |
Fixed Issue #186 - add strto(f|d|ld) overload wrappers, "-0.0" special case and FP trailing zero
Update: The example program above now outputs |
I reran the benchmark: all double tests succeed now. |
I checked the library with the latest https://github.com/miloyip/nativejson-benchmark benchmark suite. There are several errors parsing floating-point numbers:
The respective code is this:
I will have a deeper look at this later. I just wanted to paste the results here before I forget.
The text was updated successfully, but these errors were encountered: