From: Giuseppe Ottaviano Date: Sun, 26 Nov 2017 22:43:06 +0000 (-0800) Subject: Add folly::hasher support for floating point types X-Git-Tag: v2017.11.27.00~1 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=13e576ac96bfc04b412d8fc54ad5abe6f2552986 Add folly::hasher support for floating point types Summary: Move `folly::hasher` closer to feature parity with `std::hash`. This is in order to replace some instances of `folly::hash::hash_combine(...)` with `folly::Hash()(...)` (`std::hash` is the identity for integers, which makes it an unsafe default for more sophisticated hash data structures, including open-addressing hash tables). The implementation is similar to `libstdc++`'s implementation, in that we handle separately the `0` case, because `0` and `-0` have different binary representations but are equal according to `operator==`, and hash the bytes otherwise. It is probably a little faster than `libstdc++`'s implementation, that delegates a out-of-line Murmur hash routine for arbitrary buffers, while this uses a minimal inlineable machine word hashing routine. Reviewed By: yfeldblum Differential Revision: D6410713 fbshipit-source-id: 86d9e4ed8da04fffe283949825852e539ec7d5cf --- diff --git a/folly/hash/Hash.h b/folly/hash/Hash.h index 5253975a..8f0743de 100644 --- a/folly/hash/Hash.h +++ b/folly/hash/Hash.h @@ -371,11 +371,12 @@ inline uint32_t hsieh_hash32_str(const std::string& str) { } // namespace hash namespace detail { + struct integral_hasher { template size_t operator()(I const& i) const { - static_assert(sizeof(I) <= 8, "input type is too wide"); - if (sizeof(I) <= 4) { // the branch taken is known at compile time + static_assert(sizeof(I) <= 8, "Input type is too wide"); + /* constexpr */ if (sizeof(I) <= 4) { auto const i32 = static_cast(i); // impl accident: sign-extends auto const u32 = static_cast(i32); return static_cast(hash::jenkins_rev_mix32(u32)); @@ -385,6 +386,28 @@ struct integral_hasher { } } }; + +struct float_hasher { + template + size_t operator()(F const& f) const { + static_assert(sizeof(F) <= 8, "Input type is too wide"); + + if (f == F{}) { // Ensure 0 and -0 get the same hash. + return 0; + } + + /* constexpr */ if (sizeof(F) <= 4) { + uint32_t u32 = 0; + memcpy(&u32, &f, sizeof(F)); + return static_cast(hash::jenkins_rev_mix32(u32)); + } else { + uint64_t u64 = 0; + memcpy(&u64, &f, sizeof(F)); + return static_cast(hash::twang_mix64(u64)); + } + } +}; + } // namespace detail template @@ -443,6 +466,12 @@ struct hasher : detail::integral_hasher {}; template <> // char is a different type from both signed char and unsigned char struct hasher : detail::integral_hasher {}; +template <> +struct hasher : detail::float_hasher {}; + +template <> +struct hasher : detail::float_hasher {}; + template <> struct hasher { size_t operator()(const std::string& key) const { return static_cast( diff --git a/folly/hash/test/HashTest.cpp b/folly/hash/test/HashTest.cpp index 68627002..f0eb6b85 100644 --- a/folly/hash/test/HashTest.cpp +++ b/folly/hash/test/HashTest.cpp @@ -217,6 +217,24 @@ TEST(Hash, integral_types) { EXPECT_EQ(24, hashes.size()); } +TEST(Hash, float_types) { + folly::Hash hasher; + + EXPECT_EQ(hasher(0.0f), hasher(-0.0f)); + EXPECT_EQ(hasher(0.0), hasher(-0.0)); + + // Basically just confirms that things compile ok. + std::unordered_set hashes; + hashes.insert(hasher(0.0f)); + hashes.insert(hasher(0.1f)); + hashes.insert(hasher(0.2)); + hashes.insert(hasher(0.2f)); + hashes.insert(hasher(-0.3)); + hashes.insert(hasher(-0.3f)); + + EXPECT_EQ(6, hashes.size()); +} + // Not a full hasher since only handles one type class TestHasher { public: