From 1c701a70bb7e2513fb31d00fd80c920721a30c5f Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 28 May 2009 20:42:55 +0000 Subject: [PATCH] Automatically detect what float functions the compiler/library supports in hash and seperate out some of the detail headers. Merged revisions 53159-53161,53167-53169,53175,53185,53205,53247-53248,53254 via svnmerge from https://svn.boost.org/svn/boost/trunk ........ r53159 | danieljames | 2009-05-21 22:21:11 +0100 (Thu, 21 May 2009) | 1 line Move the hash limits workaround into its own file. ........ r53160 | danieljames | 2009-05-21 22:21:44 +0100 (Thu, 21 May 2009) | 1 line Move the two different hash float implementation into their own header. ........ r53161 | danieljames | 2009-05-21 22:22:04 +0100 (Thu, 21 May 2009) | 1 line Try to automatically detect which float functions are available. ........ r53167 | danieljames | 2009-05-22 07:00:56 +0100 (Fri, 22 May 2009) | 1 line Fix a typo. ........ r53168 | danieljames | 2009-05-22 07:01:19 +0100 (Fri, 22 May 2009) | 3 lines Spell out exactly which functions can be used with which types. I was hitting some ambiguity errors when the function was for the wrong type. ........ r53169 | danieljames | 2009-05-22 07:01:35 +0100 (Fri, 22 May 2009) | 1 line Some STLport fixes for hash. ........ r53175 | danieljames | 2009-05-22 14:35:56 +0100 (Fri, 22 May 2009) | 2 lines Rename struct to avoid using 'type::'type' which confuses some compilers. ........ r53185 | danieljames | 2009-05-22 20:00:35 +0100 (Fri, 22 May 2009) | 1 line Explicitly qualify 'none' to avoid confusion with boost::none. ........ r53205 | danieljames | 2009-05-23 16:21:38 +0100 (Sat, 23 May 2009) | 4 lines Try to deal with macros for frexpl and ldexpl. The error message for msvc-9.0~wm5~stlport5.2 suggests that frexpl and ldexpl are macros. ........ r53247 | danieljames | 2009-05-25 14:45:16 +0100 (Mon, 25 May 2009) | 4 lines Check for float functions with less templates. The only template mechanism now used is full specialization, so this should hopefully be more portable to compilers we don't test. ........ r53248 | danieljames | 2009-05-25 15:27:00 +0100 (Mon, 25 May 2009) | 1 line Fix a couple of clumsy errors in the last commit. ........ r53254 | danieljames | 2009-05-25 20:44:52 +0100 (Mon, 25 May 2009) | 1 line Hash change log. ........ [SVN r53361] --- hash/doc/changes.qbk | 6 + hash/test/hash_float_test.hpp | 23 +- hash/test/hash_function_pointer_test.cpp | 1 - hash/test/hash_number_test.cpp | 2 +- hash/test/hash_string_test.cpp | 1 - .../hash/detail/float_functions.hpp | 316 +++++++++++------- .../functional/hash/detail/hash_float.hpp | 164 +++------ .../hash/detail/hash_float_generic.hpp | 93 ++++++ .../functional/hash/detail/hash_float_x86.hpp | 56 ++++ .../boost/functional/hash/detail/limits.hpp | 61 ++++ include/boost/functional/hash/hash.hpp | 1 + 11 files changed, 474 insertions(+), 250 deletions(-) create mode 100644 include/boost/functional/hash/detail/hash_float_generic.hpp create mode 100644 include/boost/functional/hash/detail/hash_float_x86.hpp create mode 100644 include/boost/functional/hash/detail/limits.hpp diff --git a/hash/doc/changes.qbk b/hash/doc/changes.qbk index 1d4d6d2..5ee9150 100644 --- a/hash/doc/changes.qbk +++ b/hash/doc/changes.qbk @@ -84,4 +84,10 @@ * [@https://svn.boost.org/trac/boost/ticket/2957 Ticket 2957]: Fix configuration for vxworks. +[h2 Boost 1.40.0] + +* Automatically configure the float functions using template metaprogramming + instead of trying to configure every possibility manually. +* Workaround for when STLport doesn't support long double. + [endsect] diff --git a/hash/test/hash_float_test.hpp b/hash/test/hash_float_test.hpp index eef49b2..77c652c 100644 --- a/hash/test/hash_float_test.hpp +++ b/hash/test/hash_float_test.hpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include @@ -23,6 +23,10 @@ #pragma warning(disable:4127) // conditional expression is constant #endif +char const* float_type(float*) { return "float"; } +char const* float_type(double*) { return "double"; } +char const* float_type(long double*) { return "long double"; } + template void float_tests(char const* name, T* = 0) { @@ -36,6 +40,15 @@ void float_tests(char const* name, T* = 0) <<"boost::hash_detail::limits::digits = " <::digits<<"\n" <<"\n" + <<"boost::hash_detail::call_ldexp::float_type = " + <::float_type*)0)<<"\n" + <<"boost::call_frexp::float_type = " + <::float_type*)0)<<"\n" + <<"boost::hash_detail::call_frexp::float_type = " + <::float_type*)0)<<"\n" + <<"boost::hash_detail::select_hash_type::type = " + <::type*)0)<<"\n" + <<"\n" ; HASH_NAMESPACE::hash x1; @@ -112,6 +125,14 @@ void float_tests(char const* name, T* = 0) T half_max = max / 2; T quarter_max = max / 4; T three_quarter_max = max - quarter_max; + + // Check the limits::max is in range. + BOOST_TEST(max != half_max); + BOOST_TEST(max != quarter_max); + BOOST_TEST(max != three_quarter_max); + BOOST_TEST(half_max != quarter_max); + BOOST_TEST(half_max != three_quarter_max); + BOOST_TEST(quarter_max != three_quarter_max); BOOST_TEST(x1(max) == HASH_NAMESPACE::hash_value(max)); BOOST_TEST(x1(half_max) == HASH_NAMESPACE::hash_value(half_max)); diff --git a/hash/test/hash_function_pointer_test.cpp b/hash/test/hash_function_pointer_test.cpp index 7fa4c89..73719e2 100644 --- a/hash/test/hash_function_pointer_test.cpp +++ b/hash/test/hash_function_pointer_test.cpp @@ -13,7 +13,6 @@ #include -#include #include #include diff --git a/hash/test/hash_number_test.cpp b/hash/test/hash_number_test.cpp index 1000258..88e03bd 100644 --- a/hash/test/hash_number_test.cpp +++ b/hash/test/hash_number_test.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include diff --git a/hash/test/hash_string_test.cpp b/hash/test/hash_string_test.cpp index 85f5a0d..b3b8394 100644 --- a/hash/test/hash_string_test.cpp +++ b/hash/test/hash_string_test.cpp @@ -13,7 +13,6 @@ #include -#include #include #include #include diff --git a/include/boost/functional/hash/detail/float_functions.hpp b/include/boost/functional/hash/detail/float_functions.hpp index 69cf91a..60dbb2a 100644 --- a/include/boost/functional/hash/detail/float_functions.hpp +++ b/include/boost/functional/hash/detail/float_functions.hpp @@ -6,6 +6,7 @@ #if !defined(BOOST_FUNCTIONAL_HASH_DETAIL_FLOAT_FUNCTIONS_HPP) #define BOOST_FUNCTIONAL_HASH_DETAIL_FLOAT_FUNCTIONS_HPP +#include #include #if defined(_MSC_VER) && (_MSC_VER >= 1020) @@ -17,146 +18,221 @@ // library implementations don't support this. On some that don't, the C99 // float functions (frexpf, frexpl, etc.) are available. // -// Some of this is based on guess work. If I don't know any better I assume that -// the standard C++ overloaded functions are available. If they're not then this -// means that the argument is cast to a double and back, which is inefficient -// and will give pretty bad results for long doubles - so if you know better -// let me know. +// The following tries to automatically detect which are available. -// STLport: -#if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) -# if (defined(__GNUC__) && __GNUC__ < 3 && (defined(linux) || defined(__linux) || defined(__linux__))) || defined(__DMC__) -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -# elif defined(BOOST_MSVC) && BOOST_MSVC < 1300 -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -# else -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS -# endif +namespace boost { + namespace hash_detail { -// Roguewave: + // Returned by dummy versions of the float functions. + + struct not_found { + // Implicitly convertible to float and long double in order to avoid + // a compile error when the dummy float functions are used. + + inline operator float() const { return 0; } + inline operator long double() const { return 0; } + }; + + // A type for detecting the return type of functions. + + template struct is; + template <> struct is { char x[10]; }; + template <> struct is { char x[20]; }; + template <> struct is { char x[30]; }; + template <> struct is { char x[40]; }; + + // Used to convert the return type of a function to a type for sizeof. + + template is float_type(T); + + // call_ldexp + // + // This will get specialized for float and long double + + template struct call_ldexp + { + typedef double float_type; + + inline double operator()(double a, int b) const + { + using namespace std; + return ldexp(a, b); + } + }; + + // call_frexp + // + // This will get specialized for float and long double + + template struct call_frexp + { + typedef double float_type; + + inline double operator()(double a, int* b) const + { + using namespace std; + return frexp(a, b); + } + }; + } +} + +// A namespace for dummy functions to detect when the actual function we want +// isn't available. ldexpl, ldexpf etc. might be added tby the macros below. // -// On borland 5.51, with roguewave 2.1.1 the standard C++ overloads aren't -// defined, but for the same version of roguewave on sunpro they are. -#elif defined(_RWSTD_VER) -# if defined(__BORLANDC__) -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -# define BOOST_HASH_C99_NO_FLOAT_FUNCS -# elif defined(__DECCXX) -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -# else -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS -# endif +// AFAICT these have to be outside of the boost namespace, as if they're in +// the boost namespace they'll always be preferable to any other function +// (since the arguments are built in types, ADL can't be used). -// libstdc++ (gcc 3.0 onwards, I think) -#elif defined(__GLIBCPP__) || defined(__GLIBCXX__) -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS +namespace BOOST_HASH_DETECT_FLOAT_FUNCTIONS { + template boost::hash_detail::not_found ldexp(Float, int); + template boost::hash_detail::not_found frexp(Float, int*); +} -// SGI: -#elif defined(__STL_CONFIG_H) -# if defined(linux) || defined(__linux) || defined(__linux__) -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -# else -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS -# endif +// Macros for generating specializations of call_ldexp and call_frexp. +// +// check_cpp and check_c99 check if the C++ or C99 functions are available. +// +// Then the call_* functions select an appropriate implementation. +// +// I used c99_func in a few places just to get a unique name. -// vxWorks. It has its own math library, but uses Dinkumware STL -#elif defined(__VXWORKS__) -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS +#define BOOST_HASH_CALL_FLOAT_FUNC(cpp_func, c99_func, type1, type2) \ +namespace BOOST_HASH_DETECT_FLOAT_FUNCTIONS { \ + boost::hash_detail::not_found c99_func(int, type2); \ +} \ + \ +namespace boost { \ + namespace hash_detail { \ + namespace c99_func##_detect { \ + using namespace std; \ + using namespace BOOST_HASH_DETECT_FLOAT_FUNCTIONS; \ + \ + struct check { \ + static type1 x; \ + static type2 y; \ + BOOST_STATIC_CONSTANT(bool, cpp = \ + sizeof(float_type(cpp_func(x,y))) \ + == sizeof(is)); \ + BOOST_STATIC_CONSTANT(bool, c99 = \ + sizeof(float_type(c99_func(x,y))) \ + == sizeof(is)); \ + }; \ + \ + template \ + struct call_c99 : \ + boost::hash_detail::call_##cpp_func {}; \ + \ + template <> \ + struct call_c99 { \ + typedef type1 float_type; \ + \ + template \ + inline type1 operator()(type1 a, T b) const \ + { \ + return c99_func(a, b); \ + } \ + }; \ + \ + template \ + struct call_cpp : \ + call_c99< \ + ::boost::hash_detail::c99_func##_detect::check::c99 \ + > {}; \ + \ + template <> \ + struct call_cpp { \ + typedef type1 float_type; \ + \ + template \ + inline type1 operator()(type1 a, T b) const \ + { \ + return cpp_func(a, b); \ + } \ + }; \ + } \ + \ + template <> \ + struct call_##cpp_func : \ + c99_func##_detect::call_cpp< \ + ::boost::hash_detail::c99_func##_detect::check::cpp \ + > {}; \ + } \ +} -// Dinkumware. -#elif (defined(_YVALS) && !defined(__IBMCPP__)) || defined(_CPPLIB_VER) -// Some versions of Visual C++ don't seem to have the C++ overloads but they -// all seem to have the c99 float overloads -# if defined(BOOST_MSVC) -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -// On other platforms the C++ overloads seem to have been introduced sometime -// before 402. -# elif defined(_CPPLIB_VER) && (_CPPLIB_VER >= 402) -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS -# else -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -# endif +#define BOOST_HASH_CALL_FLOAT_MACRO(cpp_func, c99_func, type1, type2) \ +namespace boost { \ + namespace hash_detail { \ + \ + template <> \ + struct call_##cpp_func { \ + typedef type1 float_type; \ + inline type1 operator()(type1 x, type2 y) const { \ + return c99_func(x, y); \ + } \ + }; \ + } \ +} -// Digital Mars -#elif defined(__DMC__) -# define BOOST_HASH_USE_C99_FLOAT_FUNCS - -// Use overloaded float functions by default. +#if defined(ldexpf) +BOOST_HASH_CALL_FLOAT_MACRO(ldexp, ldexpf, float, int) #else -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS +BOOST_HASH_CALL_FLOAT_FUNC(ldexp, ldexpf, float, int) #endif +#if defined(ldexpl) +BOOST_HASH_CALL_FLOAT_MACRO(ldexp, ldexpl, long double, int) +#else +BOOST_HASH_CALL_FLOAT_FUNC(ldexp, ldexpl, long double, int) +#endif + +#if defined(frexpf) +BOOST_HASH_CALL_FLOAT_MACRO(frexp, frexpf, float, int*) +#else +BOOST_HASH_CALL_FLOAT_FUNC(frexp, frexpf, float, int*) +#endif + +#if defined(frexpl) +BOOST_HASH_CALL_FLOAT_MACRO(frexp, frexpl, long double, int*) +#else +BOOST_HASH_CALL_FLOAT_FUNC(frexp, frexpl, long double, int*) +#endif + +#undef BOOST_HASH_CALL_FLOAT_MACRO +#undef BOOST_HASH_CALL_FLOAT_FUNC + + namespace boost { namespace hash_detail { + template + struct select_hash_type_impl { + typedef double type; + }; - inline float call_ldexp(float v, int exp) - { - using namespace std; -#if defined(BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS) || \ - defined(BOOST_HASH_C99_NO_FLOAT_FUNCS) - return ldexp(v, exp); -#else - return ldexpf(v, exp); -#endif - } + template <> + struct select_hash_type_impl { + typedef float type; + }; - inline double call_ldexp(double v, int exp) - { - using namespace std; - return ldexp(v, exp); - } + template <> + struct select_hash_type_impl { + typedef long double type; + }; - inline long double call_ldexp(long double v, int exp) - { - using namespace std; -#if defined(BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS) - return ldexp(v, exp); -#else - return ldexpl(v, exp); -#endif - } - inline float call_frexp(float v, int* exp) - { - using namespace std; -#if defined(BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS) || \ - defined(BOOST_HASH_C99_NO_FLOAT_FUNCS) - return frexp(v, exp); -#else - return frexpf(v, exp); -#endif - } - - inline double call_frexp(double v, int* exp) - { - using namespace std; - return frexp(v, exp); - } - - inline long double call_frexp(long double v, int* exp) - { - using namespace std; -#if defined(BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS) - return frexp(v, exp); -#else - return frexpl(v, exp); -#endif - } + // select_hash_type + // + // If there is support for a particular floating point type, use that + // otherwise use double (there's always support for double). + + template + struct select_hash_type : select_hash_type_impl< + BOOST_DEDUCED_TYPENAME call_ldexp::float_type, + BOOST_DEDUCED_TYPENAME call_frexp::float_type + > {}; } } -#if defined(BOOST_HASH_USE_C99_FLOAT_FUNCS) -#undef BOOST_HASH_USE_C99_FLOAT_FUNCS -#endif - -#if defined(BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS) -#undef BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS -#endif - -#if defined(BOOST_HASH_C99_NO_FLOAT_FUNCS) -#undef BOOST_HASH_C99_NO_FLOAT_FUNCS -#endif - #endif diff --git a/include/boost/functional/hash/detail/hash_float.hpp b/include/boost/functional/hash/detail/hash_float.hpp index 5d5ac34..0137ab7 100644 --- a/include/boost/functional/hash/detail/hash_float.hpp +++ b/include/boost/functional/hash/detail/hash_float.hpp @@ -18,156 +18,57 @@ #endif #endif +#include #include +#include #include #include -#include #include -// Select implementation for the current platform. +// Include hash implementation for the current platform. // Cygwn #if defined(__CYGWIN__) # if defined(__i386__) || defined(_M_IX86) -# define BOOST_HASH_USE_x86_BINARY_HASH +# include +# else +# include # endif +#else +# include +#endif + +// Can we use fpclassify? // STLport -#elif defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) -// fpclassify aren't good enough on STLport. +#if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) +#define BOOST_HASH_USE_FPCLASSIFY 0 // GNU libstdc++ 3 #elif defined(__GLIBCPP__) || defined(__GLIBCXX__) # if (defined(__USE_ISOC99) || defined(_GLIBCXX_USE_C99_MATH)) && \ !(defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)) -# define BOOST_HASH_USE_FPCLASSIFY +# define BOOST_HASH_USE_FPCLASSIFY 1 +# else +# define BOOST_HASH_USE_FPCLASSIFY 0 # endif -// Dinkumware Library, on Visual C++ -#elif (defined(_YVALS) && !defined(__IBMCPP__)) || defined(_CPPLIB_VER) - -// Not using _fpclass because it is only available for double. - +// Everything else +#else +# define BOOST_HASH_USE_FPCLASSIFY 0 #endif -// On OpenBSD, numeric_limits is not reliable for long doubles, but -// the macros defined in are. +#if BOOST_HASH_USE_FPCLASSIFY -#if defined(__OpenBSD__) -#include -#endif +#include namespace boost { namespace hash_detail { - template - struct limits : std::numeric_limits {}; - -#if defined(__OpenBSD__) - template <> - struct limits - : std::numeric_limits - { - static long double epsilon() { - return LDBL_EPSILON; - } - - static long double (max)() { - return LDBL_MAX; - } - - static long double (min)() { - return LDBL_MIN; - } - - BOOST_STATIC_CONSTANT(int, digits = LDBL_MANT_DIG); - BOOST_STATIC_CONSTANT(int, max_exponent = LDBL_MAX_EXP); - BOOST_STATIC_CONSTANT(int, min_exponent = LDBL_MIN_EXP); - }; -#endif // __OpenBSD__ - - inline void hash_float_combine(std::size_t& seed, std::size_t value) - { - seed ^= value + (seed<<6) + (seed>>2); - } - -// A simple, non-portable hash algorithm for x86. -#if defined(BOOST_HASH_USE_x86_BINARY_HASH) - 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; - } - -#else - - template - inline std::size_t float_hash_impl(T v) - { - int exp = 0; - - v = boost::hash_detail::call_frexp(v, &exp); - - // A postive value is easier to hash, so combine the - // sign with the exponent. - if(v < 0) { - v = -v; - exp += limits::max_exponent - - limits::min_exponent; - } - - // The result of frexp is always between 0.5 and 1, so its - // top bit will always be 1. Subtract by 0.5 to remove that. - v -= T(0.5); - v = boost::hash_detail::call_ldexp(v, - limits::digits + 1); - std::size_t seed = static_cast(v); - v -= seed; - - // ceiling(digits(T) * log2(radix(T))/ digits(size_t)) - 1; - std::size_t const length - = (limits::digits * - boost::static_log2::radix>::value - 1) - / limits::digits; - - for(std::size_t i = 0; i != length; ++i) - { - v = boost::hash_detail::call_ldexp(v, - limits::digits); - std::size_t part = static_cast(v); - v -= part; - hash_float_combine(seed, part); - } - - hash_float_combine(seed, exp); - - return seed; - } -#endif - template inline std::size_t float_hash_value(T v) { -#if defined(BOOST_HASH_USE_FPCLASSIFY) using namespace std; switch (fpclassify(v)) { case FP_ZERO: @@ -183,15 +84,26 @@ namespace boost BOOST_ASSERT(0); return 0; } -#else - return v == 0 ? 0 : float_hash_impl(v); -#endif } } } -#if defined(BOOST_MSVC) -#pragma warning(pop) -#endif +#else // !BOOST_HASH_USE_FPCLASSIFY + +namespace boost +{ + namespace hash_detail + { + template + inline std::size_t float_hash_value(T v) + { + return v == 0 ? 0 : float_hash_impl(v); + } + } +} + +#endif // BOOST_HASH_USE_FPCLASSIFY + +#undef BOOST_HASH_USE_FPCLASSIFY #endif diff --git a/include/boost/functional/hash/detail/hash_float_generic.hpp b/include/boost/functional/hash/detail/hash_float_generic.hpp new file mode 100644 index 0000000..164e18a --- /dev/null +++ b/include/boost/functional/hash/detail/hash_float_generic.hpp @@ -0,0 +1,93 @@ + +// 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; + } + + // The result of frexp is always between 0.5 and 1, so its + // top bit will always be 1. Subtract by 0.5 to remove that. + v -= T(0.5); + v = ldexp(v, limits::digits + 1); + std::size_t seed = static_cast(v); + v -= seed; + + // ceiling(digits(T) * log2(radix(T))/ digits(size_t)) - 1; + std::size_t const length + = (limits::digits * + boost::static_log2::radix>::value - 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 -= 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 new file mode 100644 index 0000000..b39bb0d --- /dev/null +++ b/include/boost/functional/hash/detail/hash_float_x86.hpp @@ -0,0 +1,56 @@ + +// 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 diff --git a/include/boost/functional/hash/detail/limits.hpp b/include/boost/functional/hash/detail/limits.hpp new file mode 100644 index 0000000..f5b520e --- /dev/null +++ b/include/boost/functional/hash/detail/limits.hpp @@ -0,0 +1,61 @@ + +// 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) +// +// On some platforms std::limits gives incorrect values for long double. +// This tries to work around them. + +#if !defined(BOOST_FUNCTIONAL_HASH_DETAIL_LIMITS_HEADER) +#define BOOST_FUNCTIONAL_HASH_DETAIL_LIMITS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include + +// On OpenBSD, numeric_limits is not reliable for long doubles, but +// the macros defined in are and support long double when STLport +// doesn't. + +#if defined(__OpenBSD__) || defined(_STLP_NO_LONG_DOUBLE) +#include +#endif + +namespace boost +{ + namespace hash_detail + { + template + struct limits : std::numeric_limits {}; + +#if defined(__OpenBSD__) || defined(_STLP_NO_LONG_DOUBLE) + template <> + struct limits + : std::numeric_limits + { + static long double epsilon() { + return LDBL_EPSILON; + } + + static long double (max)() { + return LDBL_MAX; + } + + static long double (min)() { + return LDBL_MIN; + } + + BOOST_STATIC_CONSTANT(int, digits = LDBL_MANT_DIG); + BOOST_STATIC_CONSTANT(int, max_exponent = LDBL_MAX_EXP); + BOOST_STATIC_CONSTANT(int, min_exponent = LDBL_MIN_EXP); +#if defined(_STLP_NO_LONG_DOUBLE) + BOOST_STATIC_CONSTANT(int, radix = FLT_RADIX); +#endif + }; +#endif // __OpenBSD__ + } +} + +#endif diff --git a/include/boost/functional/hash/hash.hpp b/include/boost/functional/hash/hash.hpp index 67284fc..12283ff 100644 --- a/include/boost/functional/hash/hash.hpp +++ b/include/boost/functional/hash/hash.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) #include