Fix integer comparison bug#5211
Conversation
🔴 Amalgamation check failed! 🔴The source code has not been amalgamated. @ljcjclljc |
There was a problem hiding this comment.
This seems reasonable at a low level. There may be some cases that should be documented where a < b and json(a) < json(b) will provide different results. Those are the same cases where the compiler should have been warning you not to do that a < b, so that's probably okay, but good to call it out explicitly.
| { \ | ||
| return number_integer_t(1) op number_integer_t(0); \ | ||
| } \ | ||
| return lhs.m_data.m_value.number_unsigned op static_cast<number_unsigned_t>(rhs.m_data.m_value.number_integer); \ |
There was a problem hiding this comment.
This is a good change, aligns more with the C++ promotion rules.
9956862 to
19b0af7
Compare
🔴 Amalgamation check failed! 🔴The source code has not been amalgamated. @ljcjclljc |
gregmarr
left a comment
There was a problem hiding this comment.
IMO, this needs a lot more documentation, both in the main documentation section, and somewhere that will show up in the changes section of the readme as a potentially breaking change, with the appropriate rationale, as described in the linked issue.
| // Mixed signed/unsigned integer comparisons check for negative signed | ||
| // values before casting. This avoids wraparound when converting a negative | ||
| // integer to an unsigned type and preserves the rule that any negative | ||
| // integer is smaller than any unsigned integer. |
There was a problem hiding this comment.
Since we probably don't want to put a comment inside the macro explaining the actual op, should there be one here?
// This is done by checking for the signed value being less than zero.
// If it is, the comparison is performed using `0` for the signed value
// and `1` for the unsigned value. Since the negative signed value will
// always be less than the unsigned value, any two fixed values that
// have the same relationship will produce the right result.
// If the signed value is not less than zero, it is cast to unsigned
// before doing the comparison.
I'm wondering if -1 and 1 would be clearer than 0 and 1. That way you can explicitly see the negative sign.
nlohmann
left a comment
There was a problem hiding this comment.
Some comments. Will review later.
Signed-off-by: ljccjlljc <939159710@qq.com>
Signed-off-by: ljccjlljc <939159710@qq.com>
Signed-off-by: ljccjlljc <939159710@qq.com>
Signed-off-by: ljccjlljc <939159710@qq.com>
Signed-off-by: ljccjlljc <939159710@qq.com>
Signed-off-by: ljccjlljc <939159710@qq.com>
Signed-off-by: ljccjlljc <939159710@qq.com>
|
For the release notes, I would add the following to the changelog:
Furthermore, I would add somewhere notable at the top:
@gregmarr @ljcjclljc Are you OK with this? @gregmarr Anything you would like to add or discuss for this PR - #5211 (comment) is still open. |
|
I fully agree with the wording you drafted for the changelog and top highlight notice. It clearly explains the root cause, wrong examples, new correct comparison logic, and reminds users about possible ordering changes after upgrade. No further revisions or discussions needed from my side for PR #5211. |
This PR provides a minimal implementation for #5210.
Fiex #5210
Background
When comparing
number_unsignedandnumber_integer, the current implementation converts the unsigned value tonumber_integer_tbefore applying the operator.This is problematic for values above
INT64_MAX, because converting suchuint64_tvalues tonumber_integer_tchanges them into the negative signed range. As a result, valid mixed signed/unsigned comparisons can return mathematically incorrect results.Problem
The affected logic is in the mixed comparison branches of
JSON_IMPLEMENT_OPERATORininclude/nlohmann/json.hpp.Before this change, comparisons such as these could fail:
json(-1) < json(18446744073709551615ULL)returnedfalsejson(18446744073709551615ULL) == json(-1)returnedtrueThis is not limited to one construction path. The same incorrect behavior can be observed after direct construction, object assignment, array insertion, initializer-list construction, nested access, and
json::parse().Solution
This PR keeps the existing macro shape and applies the smallest possible fix in the mixed
number_unsigned/number_integercomparison branches:number_unsigned_tand compare in the unsigned domain.This avoids the unsafe
uint64_t -> int64_tconversion while preserving the existing comparison structure.Scope
This PR is intentionally minimal:
It only fixes the signed/unsigned mixed comparison bug described in #5210.
https://github.com/nlohmann/json/issuesis referenced.https://coveralls.io/github/nlohmann/jsonremained at 100%. A test case for every new line of code.https://json.nlohmann.meis updated.make amalgamate.