1 #ifndef BOOST_NUMERIC_UTILITY_HPP
2 #define BOOST_NUMERIC_UTILITY_HPP
3
4 // Copyright (c) 2015 Robert Ramey
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See
7 // accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9
10 #include <cstdint> // intmax_t, uintmax_t, uint8_t, ...
11 #include <algorithm>
12 #include <type_traits> // conditional
13 #include <limits>
14 #include <cassert>
15 #include <utility> // pair
16
17 #include <boost/integer.hpp> // (u)int_t<>::least, exact
18
19 namespace boost {
20 namespace safe_numerics {
21 namespace utility {
22
23 ///////////////////////////////////////////////////////////////////////////////
24 // used for debugging
25
26 // provokes warning message with names of type T
27 // usage - print_types<T, ...>;
28 // see https://cukic.co/2019/02/19/tmp-testing-and-debugging-templates
29
30 /*
31 template<typename Tx>
32 using print_type = typename Tx::error_message;
33 */
34 template <typename... Ts>
35 struct [[deprecated]] print_types {};
36
37 // display value of constexpr during compilation
38 // usage print_value(N) pn;
39 template<int N>
40 struct print_value
41 {
42 enum test : char {
43 value = N < 0 ? N - 256 : N + 256
44 };
45 };
46
47 #if 0
48 // static warning - same as static_assert but doesn't
49 // stop compilation.
50 template <typename T>
51 struct static_test{};
52
53 template <>
54 struct static_test<std::false_type>{
55 [[deprecated]] static_test(){}
56 };
57
58 template<typename T>
59 constexpr void static_warning(const T){
60 //using x = static_test<T>;
61 const static_test<T> x;
62 }
63
64 #endif
65
66 /*
67 // can be called by constexpr to produce a compile time
68 // trap of parameter passed is false.
69 // usage constexpr_assert(bool)
70 constexpr int constexpr_assert(const bool tf){
71 return 1 / tf;
72 }
73 */
74
75 ///////////////////////////////////////////////////////////////////////////////
76 // return an integral constant equal to the the number of bits
77 // held by some integer type (including the sign bit)
78
79 template<typename T>
80 using bits_type = std::integral_constant<
81 int,
82 std::numeric_limits<T>::digits
83 + (std::numeric_limits<T>::is_signed ? 1 : 0)
84 >;
85
86 /*
87 From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
88 Find the log base 2 of an integer with a lookup table
89
90 static const char LogTable256[256] =
91 {
92 #define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
93 -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
94 LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6),
95 LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7)
96 };
97
98 unsigned int v; // 32-bit word to find the log of
99 unsigned r; // r will be lg(v)
100 register unsigned int t, tt; // temporaries
101
102 if (tt = v >> 16)
103 {
104 r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt];
105 }
106 else
107 {
108 r = (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v];
109 }
110
111 The lookup table method takes only about 7 operations to find the log of a 32-bit value.
112 If extended for 64-bit quantities, it would take roughly 9 operations. Another operation
113 can be trimmed off by using four tables, with the possible additions incorporated into each.
114 Using int table elements may be faster, depending on your architecture.
115 */
116
117 namespace ilog2_detail {
118
119 // I've "improved" the above and recast as C++ code which depends upon
120 // the optimizer to minimize the operations. This should result in
121 // nine operations to calculate the position of the highest order
122 // bit in a 64 bit number. RR
123
ilog2(const boost::uint_t<8>::exact & t)124 constexpr static unsigned int ilog2(const boost::uint_t<8>::exact & t){
125 #define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
126 const char LogTable256[256] = {
127 static_cast<char>(-1), 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
128 LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6),
129 LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7)
130 };
131 return LogTable256[t];
132 }
ilog2(const boost::uint_t<16>::exact & t)133 constexpr static unsigned int ilog2(const boost::uint_t<16>::exact & t){
134 const boost::uint_t<8>::exact upper_half = (t >> 8);
135 return upper_half == 0
136 ? ilog2(static_cast<boost::uint_t<8>::exact>(t))
137 : 8 + ilog2(upper_half);
138 }
ilog2(const boost::uint_t<32>::exact & t)139 constexpr static unsigned int ilog2(const boost::uint_t<32>::exact & t){
140 const boost::uint_t<16>::exact upper_half = (t >> 16);
141 return upper_half == 0
142 ? ilog2(static_cast<boost::uint_t<16>::exact>(t))
143 : 16 + ilog2(upper_half);
144 }
ilog2(const boost::uint_t<64>::exact & t)145 constexpr static unsigned int ilog2(const boost::uint_t<64>::exact & t){
146 const boost::uint_t<32>::exact upper_half = (t >> 32);
147 return upper_half == 0
148 ? ilog2(static_cast<boost::uint_t<32>::exact>(t))
149 : 32 + ilog2(upper_half);
150 }
151
152 } // ilog2_detail
153
154 template<typename T>
ilog2(const T & t)155 constexpr unsigned int ilog2(const T & t){
156 // log not defined for negative numbers
157 // assert(t > 0);
158 if(t == 0)
159 return 0;
160 return ilog2_detail::ilog2(
161 static_cast<
162 typename boost::uint_t<
163 bits_type<T>::value
164 >::least
165 >(t)
166 );
167 }
168
169 // the number of bits required to render the value in x
170 // including sign bit
171 template<typename T>
significant_bits(const T & t)172 constexpr unsigned int significant_bits(const T & t){
173 return 1 + ((t < 0) ? ilog2(~t) : ilog2(t));
174 }
175
176 /*
177 // give the value t, return the number which corresponds
178 // to all 1's which is higher than that number
179 template<typename T>
180 constexpr unsigned int bits_value(const T & t){
181 const unsigned int sb = significant_bits(t);
182 const unsigned int sb_max = significant_bits(std::numeric_limits<T>::max());
183 return sb < sb_max ? ((sb << 1) - 1) : std::numeric_limits<T>::max();
184 }
185 */
186
187 ///////////////////////////////////////////////////////////////////////////////
188 // meta functions returning types
189
190 // If we use std::max in here we get internal compiler errors
191 // with MSVC (tested VC2017) ...
192
193 // Notes from https://en.cppreference.com/w/cpp/algorithm/max
194 // Capturing the result of std::max by reference if one of the parameters
195 // is rvalue produces a dangling reference if that parameter is returned.
196
197 template <class T>
198 // turns out this problem crashes all versions of gcc compilers. So
199 // make sure we return by value
200 //constexpr const T & max(
max(const T & lhs,const T & rhs)201 constexpr T max(
202 const T & lhs,
203 const T & rhs
204 ){
205 return lhs > rhs ? lhs : rhs;
206 }
207
208 // given a signed range, return type required to hold all the values
209 // in the range
210 template<
211 std::intmax_t Min,
212 std::intmax_t Max
213 >
214 using signed_stored_type = typename boost::int_t<
215 max(
216 significant_bits(Min),
217 significant_bits(Max)
218 ) + 1
219 >::least ;
220
221 // given an unsigned range, return type required to hold all the values
222 // in the range
223 template<
224 std::uintmax_t Min,
225 std::uintmax_t Max
226 >
227 // unsigned range
228 using unsigned_stored_type = typename boost::uint_t<
229 significant_bits(Max)
230 >::least;
231
232 ///////////////////////////////////////////////////////////////////////////////
233 // constexpr functions
234
235 // need our own version because official version
236 // a) is not constexpr
237 // b) is not guarenteed to handle non-assignable types
238 template<typename T>
239 constexpr std::pair<T, T>
minmax(const std::initializer_list<T> l)240 minmax(const std::initializer_list<T> l){
241 assert(l.size() > 0);
242 const T * minimum = l.begin();
243 const T * maximum = l.begin();
244 for(const T * i = l.begin(); i != l.end(); ++i){
245 if(*i < * minimum)
246 minimum = i;
247 else
248 if(* maximum < *i)
249 maximum = i;
250 }
251 return std::pair<T, T>{* minimum, * maximum};
252 }
253
254 // for any given t
255 // a) figure number of significant bits
256 // b) return a value with all significant bits set
257 // so for example:
258 // 3 == round_out(2) because
259 // 2 == 10 and 3 == 11
260 template<typename T>
round_out(const T & t)261 constexpr T round_out(const T & t){
262 if(t >= 0){
263 const std::uint8_t sb = utility::significant_bits(t);
264 return (sb < sizeof(T) * 8)
265 ? ((T)1 << sb) - 1
266 : std::numeric_limits<T>::max();
267 }
268 else{
269 const std::uint8_t sb = utility::significant_bits(~t);
270 return (sb < sizeof(T) * 8)
271 ? ~(((T)1 << sb) - 1)
272 : std::numeric_limits<T>::min();
273 }
274 }
275
276 } // utility
277 } // safe_numerics
278 } // boost
279
280 #endif // BOOST_NUMERIC_UTILITY_HPP
281