• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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