Normalize -0.0 in number hashes and test range ends

operator== compares numbers exactly since #5459, so equal numbers
share one value and convert to the same number_float_t. Update the
comment accordingly, map -0.0 to 0.0 before hashing (std::hash need
not do that), and test -0.0 and the ends of the integer ranges. Show
hash(0.0) in the docs example and note the change in the version
history.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-06 22:51:44 +02:00
1 parent 071f79b524
commit 268ddeb2f0
6 files changed
+39 -14

No files matched your search

+9 -7
View File
@@ -116,14 +116,16 @@ std::size_t hash(const BasicJsonType& j, const std::size_t depth = 0)
case BasicJsonType::value_t::number_unsigned:
case BasicJsonType::value_t::number_float:
{
// operator== converts between number_integer, number_unsigned, and
// number_float before comparing, so equal numbers of different
// internal types (0, 0U, 0.0) must hash the same. Combining a
// single shared type tag with the value converted to
// number_float_t keeps the hash consistent with operator== for
// every pair of numbers it considers equal.
// operator== compares numbers by their mathematical value across
// number_integer, number_unsigned, and number_float, so equal
// numbers of different internal types (0, 0U, 0.0) must hash the
// same. Two equal numbers have the same value, which converts to
// the same number_float_t, so all numbers share one type tag and
// hash that converted value. Adding zero turns -0.0 (equal to 0)
// into 0.0, as std::hash need not map both to the same hash.
const auto number_type = static_cast<std::size_t>(BasicJsonType::value_t::number_float);
const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
const auto value = j.template get<number_float_t>() + static_cast<number_float_t>(0);
const auto h = std::hash<number_float_t> {}(value);
return combine(number_type, h);
}