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