-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
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
Inconsistent Behaviour of NaN & Null Values #3799
Comments
Understood. However, if we have
fails because The json strings are equal, but the json objects are different, which seems like an inconsistency. |
It is behaving exactly as in the linked document:
|
Hi. I am not trying to debate whether this is documented or not. I am raising the point that the json string & json object are not consistent. On our side this leads to problems, as |
What do you propose? |
Two solutions:
|
1 is not technically possible in all cases due to the Are you not able to keep your workflows from generating NaN values and storing them into the json objects? Could you create your own wrapper function around the double assignment that does the NaN check and converts it to a null assignment? |
Hey, I came across the same case, in this example: json dict = json::object();
{
double nan = NAN;
double val = 123;
cout << "A nan: " << nan << ", val:" << val << endl;
dict["nan"] = nan;
dict["val"] = val;
}
cout << "B dict: " << dict.dump() << endl;
cout << "\tisnan: " << isnan(dict["nan"].get<double>()) << " " << isnan(dict["val"].get<double>()) << endl;
cout << "\t'nan'" << ", is_null: " << dict["nan"].is_null() << ", is_number: " << dict["nan"].is_number() << endl;
cout << "\t'val'" << ", is_null: " << dict["val"].is_null() << ", is_number: " << dict["val"].is_number() << endl;
{
double nan = dict["nan"].get<double>();
double val = dict["val"].get<double>();
cout << "C nan: " << nan << ", val:" << val << endl;
}
dict = json::parse(dict.dump());
cout << "D dict: " << dict.dump() << endl;
cout << "\t'nan'" << ", is_null: " << dict["nan"].is_null() << ", is_number: " << dict["nan"].is_number() << endl;
cout << "\t'val'" << ", is_null: " << dict["val"].is_null() << ", is_number: " << dict["val"].is_number() << endl;
{
double nan = 0;
nan = dict["nan"].get<double>(); // <------ json throw exception
cout << "E nan: " << nan << endl;
} A nan: nan, val:123
B dict: {"nan":null,"val":123.0}
isnan: 1 0
'nan', is_null: 0, is_number: 1
'val', is_null: 0, is_number: 1
C nan: nan, val:123
D dict: {"nan":null,"val":123.0}
'nan', is_null: 1, is_number: 0
'val', is_null: 0, is_number: 1
(lldb) And I send this json via internet, parse on server side, next dumps to file. Third solution (maybe optional by set define?): |
Adding an exception when assigning a
|
Any idea to proceed here without adding a breaking change? |
The crux of the problem is that the round-trip To recap, so far 3 solutions have been proposed:
I personally still think solution 2 has merit. Because irrespective of whether a Nonetheless, another solution:
So Python supports NaN and it's even enabled by default. We can control this behaviour via the string_t dump(int indent, char indent_char, bool ensure_ascii, error_handler_t, bool allow_nan);
basic_json parse(InputType&& i, parser_callback_t cb, bool allow_exceptions, bool ignore_comments, bool allow_nan); Then we can write Of course, listing all the parameters is a bit cumbersome. So maybe new overloads or new functions could help: // New dump overload like string_t dump(nan_policy_t) const
// where nan_policy_t = enum class { allow, forbid };
const json data2 = json::parse(data1.dump(nan_policy_t::allow), nan_policy_t::allow);
// New "..._with_nan" functions
const json data2 = json::parse_with_nan(data1.dump_with_nan()); However, if nlohmann::json's default for Hope this helps. |
I also ran into the same issue as OP, as my data contains NaNs that are not easily reducible to nulls. The optional syntax extension proposed by @phil-zxx in the previous post seems like a sensible solution that solves at least my specific problem and is in line with the prior art, even if it is not enabled by default. EDIT: Enabling NaN support in the parser by default is probably preferred as this change is unlikely to be breaking. |
I'm not sure if I care if its enabled by default, but could we at least add an optional flag to read/write NaN, Infinity, and -Infinity? |
As for now, the library will not support NaN values. |
Note that Boost.JSON has decided to write Inf and -Inf as 1e99999 and -1e99999, which seems to handle Inf without violating the JSON spec. They still write NaN as |
Description
Let us assume we have
nan = std::numeric_limits<double>::quiet_NaN()
andnull = nlohmann::json()
in our json object. Then the first is a number and NOT null. Whereas the second is null and NOT a number. However, in string form, both appear asnull
.So when dumping everything to a json string and then parsing it back into a json object, then both values have now become null. And neither value is a number. So when we try to convert any of them back to a
double
we gettype must be number, but is null
.Reproduction steps
See Minimal code example or https://godbolt.org/z/b9n7Eq9qv
Expected vs. actual results
Both
std::numeric_limits<double>::quiet_NaN()
andnlohmann::json()
are displayed asnull
in a json file. So anlohmann::json
object should treat both the same at any given point in time. Two solutions:std::numeric_limits<double>::quiet_NaN()
to a truenull
object (which is not a number). So that json in memory is consistent with its json string representation.null
to get cast toNaN
when converting todouble
.Since
NaN
serializes tonull
, I expect that when I convert a jsonnull
object to adouble
to getNaN
. That is, solution 2.Minimal code example
Error messages
Compiler and operating system
Linux (gcc 12.2), Windows (msvc v19.33)
Library version
3.11.1 or 3.11.2
Validation
develop
branch is used.The text was updated successfully, but these errors were encountered: