From 52b7187324eabe9e9235a2e67530c6403c2656a2 Mon Sep 17 00:00:00 2001 From: Jesse Li Date: Mon, 3 Oct 2022 11:17:00 -0700 Subject: [PATCH] [bug] Special case check for hashing -0.0 (#605) * Special case check for hashing 0 floating point numbers * Update test_hash.cpp Co-authored-by: Jesse Co-authored-by: John Wellbelove --- include/etl/hash.h | 22 ++++++++++++++++----- test/test_hash.cpp | 48 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/include/etl/hash.h b/include/etl/hash.h index 86d0b797..8f0e79ff 100644 --- a/include/etl/hash.h +++ b/include/etl/hash.h @@ -91,7 +91,7 @@ namespace etl /// Primary definition of base hash class, by default is poisoned //************************************************************************* template - struct hash_base + struct hash_base { private: hash_base(); // Can't default construct @@ -363,6 +363,10 @@ namespace etl float v; } u; + if (v == -0.0f) + { // -0.0 and 0.0 are represented differently at bit level + v = 0.0f; + } u.v = v; return u.s; @@ -393,6 +397,10 @@ namespace etl double v; } u; + if (v == -0.0) + { // -0.0 and 0.0 are represented differently at bit level + v = 0.0; + } u.v = v; return u.s; @@ -423,6 +431,10 @@ namespace etl long double v; } u; + if (v == -0.0L) + { // -0.0 and 0.0 are represented differently at bit level + v = 0.0L; + } u.v = v; return u.s; @@ -465,7 +477,7 @@ namespace etl } }; - namespace private_hash + namespace private_hash { //************************************************************************* /// Specialization for enums @@ -475,11 +487,11 @@ namespace etl { size_t operator()(T v) const { - if (sizeof(size_t) >= sizeof(T)) + if (sizeof(size_t) >= sizeof(T)) { return static_cast(v); - } - else + } + else { return ::etl::hash()(static_cast(v)); } diff --git a/test/test_hash.cpp b/test/test_hash.cpp index 8a1bd8a4..037422e1 100644 --- a/test/test_hash.cpp +++ b/test/test_hash.cpp @@ -199,6 +199,30 @@ namespace } } + TEST(test_hash_floating_point_negative_zero) + { + if (sizeof(float) == sizeof(size_t)) { + etl::hash hasher{}; + size_t hash1 = hasher(0.0); + size_t hash2 = hasher(-0.0); + CHECK_EQUAL(hash1, hash2); + } + + if (sizeof(double) == sizeof(size_t)) { + etl::hash hasher{}; + size_t hash1 = hasher(0.0); + size_t hash2 = hasher(-0.0); + CHECK_EQUAL(hash1, hash2); + } + + if (sizeof(long double) == sizeof(size_t)) { + etl::hash hasher{}; + size_t hash1 = hasher(0.0); + size_t hash2 = hasher(-0.0); + CHECK_EQUAL(hash1, hash2); + } + } + //************************************************************************* TEST(test_hash_pointer) { @@ -231,10 +255,13 @@ namespace //************************************************************************* TEST(test_hash_enums) { - enum class MyEnumClass : char { + enum class MyEnumClass : char + { OneE = 0x1E }; - enum MyEnum : char { + + enum MyEnum : char + { MyEnum_TwoF = 0x2F }; @@ -245,17 +272,23 @@ namespace CHECK_EQUAL(0x2F, hash); } - TEST(test_hash_big_enums) { + //************************************************************************* + TEST(test_hash_big_enums) + { constexpr unsigned long long big_number = 0x5AA555AA3CC333CCULL; - enum class MyBigEnumClass : unsigned long long { + enum class MyBigEnumClass : unsigned long long + { Big = big_number }; + size_t hash = etl::hash()(MyBigEnumClass::Big); size_t expectedHash = etl::hash()(big_number); CHECK_EQUAL(expectedHash, hash); } - TEST(test_hash_poisoned) { + //************************************************************************* + TEST(test_hash_poisoned) + { // Unspecialized hash<> should be disabled (unusable) - see https://en.cppreference.com/w/cpp/utility/hash class A {}; typedef etl::hash general_hasher; @@ -267,8 +300,9 @@ namespace CHECK_FALSE(std::is_move_assignable::value); } - - TEST(test_hash_custom) { + //************************************************************************* + TEST(test_hash_custom) + { typedef etl::hash custom_hasher; CHECK_TRUE(std::is_default_constructible::value);