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>