diff --git a/hash/test/Jamfile.v2 b/hash/test/Jamfile.v2 index 0f41d3e..5b7c39a 100644 --- a/hash/test/Jamfile.v2 +++ b/hash/test/Jamfile.v2 @@ -1,5 +1,5 @@ -# Copyright 2005-2011 Daniel James. +# Copyright 2005-2012 Daniel James. # Distributed under the Boost Software License, Version 1.0. (See accompanying # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -66,4 +66,17 @@ test-suite functional/hash_no_ext [ run link_test.cpp link_test_2.cpp : : : BOOST_HASH_NO_EXTENSIONS : no_ext_link_test ] ; +# Tests to see if the floating point hash is using the binary hash. +# Not run normally because on some platforms these should fail. +test-suite functional/hash_no_generic_float + : + [ run hash_float_test.cpp + : : : BOOST_HASH_DETAIL_TEST_WITHOUT_GENERIC + : hash_float_test_no_generic ] + [ run hash_long_double_test.cpp + : : : BOOST_HASH_DETAIL_TEST_WITHOUT_GENERIC + : hash_long_double_test_no_generic ] + ; +explicit functional/hash_no_generic_float ; + build-project ../examples ; diff --git a/include/boost/functional/hash/detail/hash_float.hpp b/include/boost/functional/hash/detail/hash_float.hpp index 194be1c..7ccfb80 100644 --- a/include/boost/functional/hash/detail/hash_float.hpp +++ b/include/boost/functional/hash/detail/hash_float.hpp @@ -1,5 +1,5 @@ -// Copyright 2005-2009 Daniel James. +// Copyright 2005-2012 Daniel James. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,21 +13,19 @@ #include #include #include +#include #include #include #include +#include +#include -// Include hash implementation for the current platform. - -// Cygwn -#if defined(__CYGWIN__) -# if defined(__i386__) || defined(_M_IX86) -# include -# else -# include -# endif -#else -# include +#if defined(BOOST_MSVC) +#pragma warning(push) +#if BOOST_MSVC >= 1400 +#pragma warning(disable:6294) // Ill-defined for-loop: initial condition does + // not satisfy test. Loop body not executed +#endif #endif // Can we use fpclassify? @@ -50,6 +48,158 @@ # define BOOST_HASH_USE_FPCLASSIFY 0 #endif +namespace boost +{ + namespace hash_detail + { + inline void hash_float_combine(std::size_t& seed, std::size_t value) + { + seed ^= value + (seed<<6) + (seed>>2); + } + + //////////////////////////////////////////////////////////////////////// + // Binary hash function + // + // Only used for floats with known iec559 floats, and certain values in + // numeric_limits + + inline std::size_t hash_binary(char* ptr, std::size_t length) + { + std::size_t seed = 0; + + if (length >= sizeof(std::size_t)) { + seed = *(std::size_t*) ptr; + length -= sizeof(std::size_t); + ptr += sizeof(std::size_t); + + while(length >= sizeof(std::size_t)) { + hash_float_combine(seed, *(std::size_t*) ptr); + length -= sizeof(std::size_t); + ptr += sizeof(std::size_t); + } + } + + if (length > 0) { + std::size_t buffer = 0; + std::memcpy(&buffer, ptr, length); + hash_float_combine(seed, buffer); + } + + return seed; + } + + template + inline std::size_t float_hash_impl(Float v, + BOOST_DEDUCED_TYPENAME boost::enable_if_c< + std::numeric_limits::is_iec559 && + std::numeric_limits::digits == 24 && + std::numeric_limits::radix == 2 && + std::numeric_limits::max_exponent == 128, + int>::type + ) + { + boost::uint32_t* ptr = (boost::uint32_t*)&v; + return (std::size_t) *ptr; + } + + + template + inline std::size_t float_hash_impl(Float v, + BOOST_DEDUCED_TYPENAME boost::enable_if_c< + std::numeric_limits::is_iec559 && + std::numeric_limits::digits == 53 && + std::numeric_limits::radix == 2 && + std::numeric_limits::max_exponent == 1024, + int>::type + ) + { + return hash_binary((char*) &v, 8); + } + + template + inline std::size_t float_hash_impl(Float v, + BOOST_DEDUCED_TYPENAME boost::enable_if_c< + std::numeric_limits::is_iec559 && + std::numeric_limits::digits == 64 && + std::numeric_limits::radix == 2 && + std::numeric_limits::max_exponent == 16384, + int>::type + ) + { + return hash_binary((char*) &v, 10); + } + + template + inline std::size_t float_hash_impl(Float v, + BOOST_DEDUCED_TYPENAME boost::enable_if_c< + std::numeric_limits::is_iec559 && + std::numeric_limits::digits == 113 && + std::numeric_limits::radix == 2 && + std::numeric_limits::max_exponent == 16384, + int>::type + ) + { + return hash_binary((char*) &v, 16); + } + + //////////////////////////////////////////////////////////////////////// + // Portable hash function + // + // Used as a fallback when the binary hash function isn't supported. + + template + inline std::size_t float_hash_impl2(T v) + { + boost::hash_detail::call_frexp frexp; + boost::hash_detail::call_ldexp ldexp; + + int exp = 0; + + v = frexp(v, &exp); + + // A postive value is easier to hash, so combine the + // sign with the exponent and use the absolute value. + if(v < 0) { + v = -v; + exp += limits::max_exponent - + limits::min_exponent; + } + + v = ldexp(v, limits::digits); + std::size_t seed = static_cast(v); + v -= static_cast(seed); + + // ceiling(digits(T) * log2(radix(T))/ digits(size_t)) - 1; + std::size_t const length + = (limits::digits * + boost::static_log2::radix>::value + + limits::digits - 1) + / limits::digits; + + for(std::size_t i = 0; i != length; ++i) + { + v = ldexp(v, limits::digits); + std::size_t part = static_cast(v); + v -= static_cast(part); + hash_float_combine(seed, part); + } + + hash_float_combine(seed, exp); + + return seed; + } + +#if !defined(BOOST_HASH_DETAIL_TEST_WITHOUT_GENERIC) + template + inline std::size_t float_hash_impl(T v, ...) + { + typedef BOOST_DEDUCED_TYPENAME select_hash_type::type type; + return float_hash_impl2(static_cast(v)); + } +#endif + } +} + #if BOOST_HASH_USE_FPCLASSIFY #include @@ -71,7 +221,7 @@ namespace boost return (std::size_t)(-3); case FP_NORMAL: case FP_SUBNORMAL: - return float_hash_impl(v); + return float_hash_impl(v, 0); default: BOOST_ASSERT(0); return 0; @@ -103,7 +253,7 @@ namespace boost template inline std::size_t float_hash_value(T v) { - return boost::hash_detail::is_zero(v) ? 0 : float_hash_impl(v); + return boost::hash_detail::is_zero(v) ? 0 : float_hash_impl(v, 0); } } } @@ -112,4 +262,8 @@ namespace boost #undef BOOST_HASH_USE_FPCLASSIFY +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + #endif diff --git a/include/boost/functional/hash/detail/hash_float_generic.hpp b/include/boost/functional/hash/detail/hash_float_generic.hpp deleted file mode 100644 index 1278c2f..0000000 --- a/include/boost/functional/hash/detail/hash_float_generic.hpp +++ /dev/null @@ -1,91 +0,0 @@ - -// Copyright 2005-2009 Daniel James. -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -// A general purpose hash function for non-zero floating point values. - -#if !defined(BOOST_FUNCTIONAL_HASH_DETAIL_HASH_FLOAT_GENERIC_HEADER) -#define BOOST_FUNCTIONAL_HASH_DETAIL_HASH_FLOAT_GENERIC_HEADER - -#include -#include -#include - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -#if defined(BOOST_MSVC) -#pragma warning(push) -#if BOOST_MSVC >= 1400 -#pragma warning(disable:6294) // Ill-defined for-loop: initial condition does - // not satisfy test. Loop body not executed -#endif -#endif - -namespace boost -{ - namespace hash_detail - { - inline void hash_float_combine(std::size_t& seed, std::size_t value) - { - seed ^= value + (seed<<6) + (seed>>2); - } - - template - inline std::size_t float_hash_impl2(T v) - { - boost::hash_detail::call_frexp frexp; - boost::hash_detail::call_ldexp ldexp; - - int exp = 0; - - v = frexp(v, &exp); - - // A postive value is easier to hash, so combine the - // sign with the exponent and use the absolute value. - if(v < 0) { - v = -v; - exp += limits::max_exponent - - limits::min_exponent; - } - - v = ldexp(v, limits::digits); - std::size_t seed = static_cast(v); - v -= static_cast(seed); - - // ceiling(digits(T) * log2(radix(T))/ digits(size_t)) - 1; - std::size_t const length - = (limits::digits * - boost::static_log2::radix>::value - + limits::digits - 1) - / limits::digits; - - for(std::size_t i = 0; i != length; ++i) - { - v = ldexp(v, limits::digits); - std::size_t part = static_cast(v); - v -= static_cast(part); - hash_float_combine(seed, part); - } - - hash_float_combine(seed, exp); - - return seed; - } - - template - inline std::size_t float_hash_impl(T v) - { - typedef BOOST_DEDUCED_TYPENAME select_hash_type::type type; - return float_hash_impl2(static_cast(v)); - } - } -} - -#if defined(BOOST_MSVC) -#pragma warning(pop) -#endif - -#endif diff --git a/include/boost/functional/hash/detail/hash_float_x86.hpp b/include/boost/functional/hash/detail/hash_float_x86.hpp deleted file mode 100644 index b39bb0d..0000000 --- a/include/boost/functional/hash/detail/hash_float_x86.hpp +++ /dev/null @@ -1,56 +0,0 @@ - -// Copyright 2005-2009 Daniel James. -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -// A non-portable hash function form non-zero floats on x86. -// -// Even if you're on an x86 platform, this might not work if their floating -// point isn't set up as this expects. So this should only be used if it's -// absolutely certain that it will work. - -#if !defined(BOOST_FUNCTIONAL_HASH_DETAIL_HASH_FLOAT_X86_HEADER) -#define BOOST_FUNCTIONAL_HASH_DETAIL_HASH_FLOAT_X86_HEADER - -#include - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -namespace boost -{ - namespace hash_detail - { - inline void hash_float_combine(std::size_t& seed, std::size_t value) - { - seed ^= value + (seed<<6) + (seed>>2); - } - - inline std::size_t float_hash_impl(float v) - { - boost::uint32_t* ptr = (boost::uint32_t*)&v; - std::size_t seed = *ptr; - return seed; - } - - inline std::size_t float_hash_impl(double v) - { - boost::uint32_t* ptr = (boost::uint32_t*)&v; - std::size_t seed = *ptr++; - hash_float_combine(seed, *ptr); - return seed; - } - - inline std::size_t float_hash_impl(long double v) - { - boost::uint32_t* ptr = (boost::uint32_t*)&v; - std::size_t seed = *ptr++; - hash_float_combine(seed, *ptr++); - hash_float_combine(seed, *(boost::uint16_t*)ptr); - return seed; - } - } -} - -#endif