mirror of
https://github.com/nlohmann/json.git
synced 2026-07-29 23:23:02 +04:00
Fix std::hash contract violation for numeric types
Fixes #5256: json(42) == json(42u) is true, but their hashes differed, violating the std::hash contract. This also applied to float comparisons: json(42) == json(42.0) is true, but they hashed differently. Solution: Normalize numeric type hashing to ensure equal values hash equal. - Signed/unsigned integers: normalize unsigned to signed via static_cast, matching the existing operator== behavior (lines 3711-3717 in json.hpp) - Integer/float bridging: for values exactly representable as the float type, hash via the float form to collide correctly with float values - All numeric types share a single type tag to ensure hash collision The fix is rigorous for the reported issue (int/uint, any magnitude) with zero gaps. For int/float comparisons, there's a documented edge case at extreme magnitudes due to float precision limits, mirroring limitations already present in operator==. Changes: - include/nlohmann/detail/hash.hpp: core fix with new is_exactly_representable_as_float helper - tests/src/unit-hash.cpp: update expected hash counts (21 -> 19 distinct), add explicit std::hash contract verification - docs/mkdocs/docs/api/basic_json/std_hash.md: update description - docs/mkdocs/docs/examples/std_hash.cpp/.output: show the fix in action - single_include/nlohmann/json.hpp: regenerated via amalgamate Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -6,9 +6,18 @@ namespace std {
|
||||
}
|
||||
```
|
||||
|
||||
Return a hash value for a JSON object. The hash function tries to rely on `std::hash` where possible. Furthermore, the
|
||||
type of the JSON value is taken into account to have different hash values for `#!json null`, `#!cpp 0`, `#!cpp 0U`, and
|
||||
`#!cpp false`, etc.
|
||||
Return a hash value for a JSON object. The hash function tries to rely on `std::hash` where possible. To satisfy the
|
||||
`std::hash` contract, numeric JSON values that compare equal must hash to the same value. This means:
|
||||
|
||||
- `json(42)`, `json(42u)`, and `json(42.0)` all hash to the same value
|
||||
- `json(0)`, `json(0u)`, and `json(0.0)` all hash to the same value
|
||||
|
||||
Different types hash differently for non-numeric types (e.g., `#!json null`, `#!cpp false`, and strings all have distinct hashes).
|
||||
|
||||
**Edge case:** For very large integers outside the exact representable range of the floating-point type (beyond ~2^53 for
|
||||
typical `double`), the hash values for integer and floating-point values may differ, even if the floating-point value
|
||||
was obtained by casting the integer (due to precision loss). This is a documented limitation arising from how the
|
||||
comparison operator normalizes numeric types.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
Reference in New Issue
Block a user