hash_float.hpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // Copyright 2005-2012 Daniel James.
  2. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  3. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  4. #if !defined(BOOST_FUNCTIONAL_HASH_DETAIL_HASH_FLOAT_HEADER)
  5. #define BOOST_FUNCTIONAL_HASH_DETAIL_HASH_FLOAT_HEADER
  6. #include <boost/config.hpp>
  7. #if defined(BOOST_HAS_PRAGMA_ONCE)
  8. #pragma once
  9. #endif
  10. #include <boost/container_hash/detail/float_functions.hpp>
  11. #include <boost/container_hash/detail/limits.hpp>
  12. #include <boost/core/enable_if.hpp>
  13. #include <boost/integer/static_log2.hpp>
  14. #include <boost/cstdint.hpp>
  15. #include <boost/assert.hpp>
  16. #include <boost/limits.hpp>
  17. #include <cstring>
  18. #if defined(BOOST_MSVC)
  19. #pragma warning(push)
  20. #if BOOST_MSVC >= 1400
  21. #pragma warning(disable:6294) // Ill-defined for-loop: initial condition does
  22. // not satisfy test. Loop body not executed
  23. #endif
  24. #endif
  25. // Can we use fpclassify?
  26. // STLport
  27. #if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)
  28. #define BOOST_HASH_USE_FPCLASSIFY 0
  29. // GNU libstdc++ 3
  30. #elif defined(__GLIBCPP__) || defined(__GLIBCXX__)
  31. # if (defined(__USE_ISOC99) || defined(_GLIBCXX_USE_C99_MATH)) && \
  32. !(defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__))
  33. # define BOOST_HASH_USE_FPCLASSIFY 1
  34. # else
  35. # define BOOST_HASH_USE_FPCLASSIFY 0
  36. # endif
  37. // Everything else
  38. #else
  39. # define BOOST_HASH_USE_FPCLASSIFY 0
  40. #endif
  41. namespace boost
  42. {
  43. namespace hash_detail
  44. {
  45. inline void hash_float_combine(std::size_t& seed, std::size_t value)
  46. {
  47. seed ^= value + (seed<<6) + (seed>>2);
  48. }
  49. ////////////////////////////////////////////////////////////////////////
  50. // Binary hash function
  51. //
  52. // Only used for floats with known iec559 floats, and certain values in
  53. // numeric_limits
  54. inline std::size_t hash_binary(char* ptr, std::size_t length)
  55. {
  56. std::size_t seed = 0;
  57. if (length >= sizeof(std::size_t)) {
  58. std::memcpy(&seed, ptr, sizeof(std::size_t));
  59. length -= sizeof(std::size_t);
  60. ptr += sizeof(std::size_t);
  61. while(length >= sizeof(std::size_t)) {
  62. std::size_t buffer = 0;
  63. std::memcpy(&buffer, ptr, sizeof(std::size_t));
  64. hash_float_combine(seed, buffer);
  65. length -= sizeof(std::size_t);
  66. ptr += sizeof(std::size_t);
  67. }
  68. }
  69. if (length > 0) {
  70. std::size_t buffer = 0;
  71. std::memcpy(&buffer, ptr, length);
  72. hash_float_combine(seed, buffer);
  73. }
  74. return seed;
  75. }
  76. template <typename Float, unsigned digits, unsigned max_exponent>
  77. struct enable_binary_hash
  78. {
  79. BOOST_STATIC_CONSTANT(bool, value =
  80. std::numeric_limits<Float>::is_iec559 &&
  81. std::numeric_limits<Float>::digits == digits &&
  82. std::numeric_limits<Float>::radix == 2 &&
  83. std::numeric_limits<Float>::max_exponent == max_exponent);
  84. };
  85. template <typename Float>
  86. inline std::size_t float_hash_impl(Float v,
  87. BOOST_DEDUCED_TYPENAME boost::enable_if_c<
  88. enable_binary_hash<Float, 24, 128>::value,
  89. std::size_t>::type)
  90. {
  91. return hash_binary((char*) &v, 4);
  92. }
  93. template <typename Float>
  94. inline std::size_t float_hash_impl(Float v,
  95. BOOST_DEDUCED_TYPENAME boost::enable_if_c<
  96. enable_binary_hash<Float, 53, 1024>::value,
  97. std::size_t>::type)
  98. {
  99. return hash_binary((char*) &v, 8);
  100. }
  101. template <typename Float>
  102. inline std::size_t float_hash_impl(Float v,
  103. BOOST_DEDUCED_TYPENAME boost::enable_if_c<
  104. enable_binary_hash<Float, 64, 16384>::value,
  105. std::size_t>::type)
  106. {
  107. return hash_binary((char*) &v, 10);
  108. }
  109. template <typename Float>
  110. inline std::size_t float_hash_impl(Float v,
  111. BOOST_DEDUCED_TYPENAME boost::enable_if_c<
  112. enable_binary_hash<Float, 113, 16384>::value,
  113. std::size_t>::type)
  114. {
  115. return hash_binary((char*) &v, 16);
  116. }
  117. ////////////////////////////////////////////////////////////////////////
  118. // Portable hash function
  119. //
  120. // Used as a fallback when the binary hash function isn't supported.
  121. template <class T>
  122. inline std::size_t float_hash_impl2(T v)
  123. {
  124. boost::hash_detail::call_frexp<T> frexp;
  125. boost::hash_detail::call_ldexp<T> ldexp;
  126. int exp = 0;
  127. v = frexp(v, &exp);
  128. // A postive value is easier to hash, so combine the
  129. // sign with the exponent and use the absolute value.
  130. if(v < 0) {
  131. v = -v;
  132. exp += limits<T>::max_exponent -
  133. limits<T>::min_exponent;
  134. }
  135. v = ldexp(v, limits<std::size_t>::digits);
  136. std::size_t seed = static_cast<std::size_t>(v);
  137. v -= static_cast<T>(seed);
  138. // ceiling(digits(T) * log2(radix(T))/ digits(size_t)) - 1;
  139. std::size_t const length
  140. = (limits<T>::digits *
  141. boost::static_log2<limits<T>::radix>::value
  142. + limits<std::size_t>::digits - 1)
  143. / limits<std::size_t>::digits;
  144. for(std::size_t i = 0; i != length; ++i)
  145. {
  146. v = ldexp(v, limits<std::size_t>::digits);
  147. std::size_t part = static_cast<std::size_t>(v);
  148. v -= static_cast<T>(part);
  149. hash_float_combine(seed, part);
  150. }
  151. hash_float_combine(seed, static_cast<std::size_t>(exp));
  152. return seed;
  153. }
  154. #if !defined(BOOST_HASH_DETAIL_TEST_WITHOUT_GENERIC)
  155. template <class T>
  156. inline std::size_t float_hash_impl(T v, ...)
  157. {
  158. typedef BOOST_DEDUCED_TYPENAME select_hash_type<T>::type type;
  159. return float_hash_impl2(static_cast<type>(v));
  160. }
  161. #endif
  162. }
  163. }
  164. #if BOOST_HASH_USE_FPCLASSIFY
  165. #include <boost/config/no_tr1/cmath.hpp>
  166. namespace boost
  167. {
  168. namespace hash_detail
  169. {
  170. template <class T>
  171. inline std::size_t float_hash_value(T v)
  172. {
  173. #if defined(fpclassify)
  174. switch (fpclassify(v))
  175. #elif BOOST_HASH_CONFORMANT_FLOATS
  176. switch (std::fpclassify(v))
  177. #else
  178. using namespace std;
  179. switch (fpclassify(v))
  180. #endif
  181. {
  182. case FP_ZERO:
  183. return 0;
  184. case FP_INFINITE:
  185. return (std::size_t)(v > 0 ? -1 : -2);
  186. case FP_NAN:
  187. return (std::size_t)(-3);
  188. case FP_NORMAL:
  189. case FP_SUBNORMAL:
  190. return float_hash_impl(v, 0);
  191. default:
  192. BOOST_ASSERT(0);
  193. return 0;
  194. }
  195. }
  196. }
  197. }
  198. #else // !BOOST_HASH_USE_FPCLASSIFY
  199. namespace boost
  200. {
  201. namespace hash_detail
  202. {
  203. template <class T>
  204. inline bool is_zero(T v)
  205. {
  206. #if !defined(__GNUC__) && !defined(__clang__)
  207. return v == 0;
  208. #else
  209. // GCC's '-Wfloat-equal' will complain about comparing
  210. // v to 0, but because it disables warnings for system
  211. // headers it won't complain if you use std::equal_to to
  212. // compare with 0. Resulting in this silliness:
  213. return std::equal_to<T>()(v, 0);
  214. #endif
  215. }
  216. template <class T>
  217. inline std::size_t float_hash_value(T v)
  218. {
  219. return boost::hash_detail::is_zero(v) ? 0 : float_hash_impl(v, 0);
  220. }
  221. }
  222. }
  223. #endif // BOOST_HASH_USE_FPCLASSIFY
  224. #undef BOOST_HASH_USE_FPCLASSIFY
  225. #if defined(BOOST_MSVC)
  226. #pragma warning(pop)
  227. #endif
  228. #endif