diff --git a/include/boost/decimal/hash.hpp b/include/boost/decimal/hash.hpp index 718d145ed..15a8cf50f 100644 --- a/include/boost/decimal/hash.hpp +++ b/include/boost/decimal/hash.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include @@ -26,8 +27,10 @@ struct hash // Since the underlying type is a std::uint32_t, we will rely on its hash function from the STL auto operator()(const boost::decimal::decimal32_t& v) const noexcept -> std::size_t { + const auto normalized_v {boost::decimal::normalize(v)}; + std::uint32_t bits; - std::memcpy(&bits, &v, sizeof(std::uint32_t)); + std::memcpy(&bits, &normalized_v, sizeof(std::uint32_t)); return std::hash{}(bits); } @@ -39,8 +42,10 @@ struct hash // Since the underlying type is a std::uint64_t, we will rely on its hash function from the STL auto operator()(const boost::decimal::decimal64_t& v) const noexcept -> std::size_t { + const auto normalized_v {boost::decimal::normalize(v)}; + std::uint64_t bits; - std::memcpy(&bits, &v, sizeof(std::uint64_t)); + std::memcpy(&bits, &normalized_v, sizeof(std::uint64_t)); return std::hash{}(bits); } @@ -57,8 +62,10 @@ struct hash // Take the xor of the two words and hash that auto operator()(const boost::decimal::decimal128_t& v) const noexcept -> std::size_t { + const auto normalized_v {boost::decimal::normalize(v)}; + boost::int128::uint128_t bits; - std::memcpy(&bits, &v, sizeof(boost::int128::uint128_t)); + std::memcpy(&bits, &normalized_v, sizeof(boost::int128::uint128_t)); return std::hash{}(bits.high ^ bits.low); } diff --git a/test/test_hash.cpp b/test/test_hash.cpp index 539716896..f1272b775 100644 --- a/test/test_hash.cpp +++ b/test/test_hash.cpp @@ -14,6 +14,7 @@ #include #include #include +#include template void test_hash() @@ -26,6 +27,31 @@ void test_hash() } } +// See: https://github.com/cppalliance/decimal/issues/1120 +template +void test_hash_cohorts() +{ + const std::array values { + T {3, 7}, + T {30, 6}, + T {300, 5}, + T {3000, 4}, + T {30000, 3}, + T {300000, 2}, + T {3000000, 1} + }; + + std::hash hasher; + + for (const auto val1 : values) + { + for (const auto val2 : values) + { + BOOST_TEST_EQ(hasher(val1), hasher(val2)); + } + } +} + int main() { test_hash(); @@ -35,5 +61,12 @@ int main() test_hash(); test_hash(); + test_hash_cohorts(); + test_hash_cohorts(); + test_hash_cohorts(); + test_hash_cohorts(); + test_hash_cohorts(); + test_hash_cohorts(); + return boost::report_errors(); }