fixed_point (deprecated)  rev.2
Binary Fixed-Point Arithmetic Library in C++
num_traits.h
1 
2 // Copyright John McFarlane 2015 - 2017.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file ../LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 
9 
10 #if !defined(SG14_NUMERIC_TRAITS)
11 #define SG14_NUMERIC_TRAITS 1
12 
13 #if !defined(SG14_GODBOLT_ORG)
14 
15 #include "bits/limits.h"
16 #include "bits/type_traits.h"
17 
18 #endif
19 
20 #include <utility>
21 
23 namespace sg14 {
24 
25  using _digits_type = int;
26 
28  // sg14::is_composite (default specialization)
29 
30  template<class T, class Enable = void>
31  struct is_composite : std::false_type {
32  static_assert(!std::is_reference<T>::value, "T is a reference");
33  static_assert(!std::is_const<T>::value, "T is const");
34  static_assert(!std::is_volatile<T>::value, "T is volatile");
35  };
36 
37 #if (__cplusplus > 201402L)
38  template<class T>
39  constexpr auto is_composite_v = is_composite<T>::value;
40 #endif
41 
42  namespace _num_traits_impl {
44  // sg14::_num_traits_impl::are_composite
45 
46  template<class ... Args>
47  struct are_composite;
48 
49  template<>
50  struct are_composite<> : std::false_type {
51  };
52 
53  template<class ArgHead, class ... ArgTail>
54  struct are_composite<ArgHead, ArgTail...>
55  : std::integral_constant<bool, is_composite<typename std::decay<ArgHead>::type>::value || are_composite<ArgTail...>::value> {
56  };
57 
59  // sg14::_num_traits_impl::enable_for_range
60 
61  template<_digits_type MinNumDigits, class Smaller, class T>
62  struct enable_for_range
63  : std::enable_if<MinNumDigits <= std::numeric_limits<T>::digits &&
64  std::numeric_limits<Smaller>::digits < MinNumDigits> {
65  };
66 
67  template<_digits_type MinNumDigits, class Smallest>
68  struct enable_for_range<MinNumDigits, void, Smallest>
69  : std::enable_if<MinNumDigits <= std::numeric_limits<Smallest>::digits> {
70  };
71 
72  template<_digits_type MinNumDigits, class Smaller, class T>
73  using enable_for_range_t = typename enable_for_range<MinNumDigits, Smaller, T>::type;
74 
76  // sg14::_num_traits_impl::set_digits_signed
77 
78  template<_digits_type MinNumDigits, class Enable = void>
79  struct set_digits_signed;
80 
81  template<_digits_type MinNumDigits>
82  struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, void, std::int8_t>> {
83  using type = std::int8_t;
84  };
85 
86  template<_digits_type MinNumDigits>
87  struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, std::int8_t, std::int16_t>> {
88  using type = std::int16_t;
89  };
90 
91  template<_digits_type MinNumDigits>
92  struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, std::int16_t, std::int32_t>> {
93  using type = std::int32_t;
94  };
95 
96  template<_digits_type MinNumDigits>
97  struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, std::int32_t, std::int64_t>> {
98  using type = std::int64_t;
99  };
100 
101 #if defined(SG14_INT128_ENABLED)
102  template<_digits_type MinNumDigits>
103  struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, std::int64_t, SG14_INT128>> {
104  using type = SG14_INT128;
105  };
106 #endif
107 
109  // sg14::_num_traits_impl::set_digits_unsigned
110 
111  template<_digits_type MinNumDigits, class Enable = void>
112  struct set_digits_unsigned;
113 
114  template<_digits_type MinNumDigits>
115  struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, void, std::uint8_t>> {
116  using type = std::uint8_t;
117  };
118 
119  template<_digits_type MinNumDigits>
120  struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, std::uint8_t, std::uint16_t>> {
121  using type = std::uint16_t;
122  };
123 
124  template<_digits_type MinNumDigits>
125  struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, std::uint16_t, std::uint32_t>> {
126  using type = std::uint32_t;
127  };
128 
129  template<_digits_type MinNumDigits>
130  struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, std::uint32_t, std::uint64_t>> {
131  using type = std::uint64_t;
132  };
133 
134 #if defined(SG14_INT128_ENABLED)
135  template<_digits_type MinNumDigits>
136  struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, std::uint64_t, SG14_UINT128>> {
137  using type = SG14_UINT128;
138  };
139 #endif
140 
142  // sg14::_num_traits_impl::set_digits_integer
143 
144  template<class Integer, _digits_type MinNumDigits>
145  using set_digits_integer = typename std::conditional<
146  std::numeric_limits<Integer>::is_signed,
147  set_digits_signed<MinNumDigits>,
148  set_digits_unsigned<MinNumDigits>>::type;
149  }
150 
152  // digits
153 
154  template<class T, class Enable = void>
155  struct digits : std::integral_constant<_digits_type, std::numeric_limits<T>::digits> {
156  };
157 
158 #if (__cplusplus > 201402L)
159  template<class T>
160  constexpr _digits_type digits_v = digits<T>::value;
161 #endif
162 
164  // set_digits / set_digits_t
165 
166  template<class T, _digits_type Digits, class Enable = void>
167  struct set_digits;
168 
169  template<class T, _digits_type Digits>
170  struct set_digits<T, Digits, _impl::enable_if_t<std::is_integral<T>::value>>
171  : _num_traits_impl::set_digits_integer<T, Digits> {
172  };
173 
174 #if defined(SG14_INT128_ENABLED)
175  template<_digits_type Digits>
176  struct set_digits<SG14_INT128, Digits>
177  : _num_traits_impl::set_digits_integer<signed, Digits> {
178  };
179 
180  template<_digits_type Digits>
181  struct set_digits<SG14_UINT128, Digits>
182  : _num_traits_impl::set_digits_integer<unsigned, Digits> {
183  };
184 #endif
185 
186  template<class T, _digits_type Digits>
187  using set_digits_t = typename set_digits<T, Digits>::type;
188 
190  // sg14::is_unsigned
191 
192  template<class T>
193  struct is_signed : std::integral_constant<bool, std::numeric_limits<T>::is_signed> {
194  };
195 
197  // sg14::make_signed
198 
199  template<class, class = void>
200  struct make_signed;
201 
202  template<class T>
203  struct make_signed<T, _impl::enable_if_t<std::is_integral<T>::value>> : std::make_signed<T> {
204  };
205 
206  template<class T>
207  using make_signed_t = typename make_signed<T>::type;
208 
210  // sg14::make_unsigned
211 
212  template<class, class = void>
213  struct make_unsigned;
214 
215  template<class T>
216  struct make_unsigned<T, _impl::enable_if_t<std::is_integral<T>::value>> : std::make_unsigned<T> {
217  };
218 
219 #if defined(SG14_INT128_ENABLED)
220  // TODO: sg14::is_integral
221  template<>
222  struct make_unsigned<SG14_INT128> {
223  using type = SG14_UINT128;
224  };
225  template<>
226  struct make_unsigned<SG14_UINT128> {
227  using type = SG14_UINT128;
228  };
229 
230  template<>
231  struct make_signed<SG14_INT128> {
232  using type = SG14_INT128;
233  };
234  template<>
235  struct make_signed<SG14_UINT128> {
236  using type = SG14_INT128;
237  };
238 #endif
239 
240  template<class T>
241  using make_unsigned_t = typename make_unsigned<T>::type;
242 
243  namespace _impl {
245  // sg14::_impl::make_signed - std::make_signed with IsSigned parameter
246 
247  template<class T, bool IsSigned = true>
248  struct make_signed;
249 
250  template<class T>
251  struct make_signed<T, true> : ::sg14::make_signed<T> {
252  };
253 
254  template<class T>
255  struct make_signed<T, false> : ::sg14::make_unsigned<T> {
256  };
257 
258  template<class T, bool IsSigned>
259  using make_signed_t = typename make_signed<T, IsSigned>::type;
260 
262  // sg14::_impl::common_signedness
263 
264  template<class T1, class T2>
265  struct common_signedness {
266  static constexpr bool _are_signed = std::numeric_limits<T1>::is_signed | std::numeric_limits<T2>::is_signed;
267 
268  using type = typename std::common_type<make_signed_t<T1, _are_signed>,
269  make_signed_t<T2, _are_signed>>::type;
270  };
271 
272  template<class T1, class T2>
273  using common_signedness_t = typename common_signedness<T1, T2>::type;
274 
276  // sg14::_impl::encompasses
277 
278  template<class T, class Enable = void>
279  struct unsigned_or_float;
280 
281  template<class T>
282  struct unsigned_or_float<T, enable_if_t<std::numeric_limits<T>::is_iec559>> {
283  using type = T;
284  };
285 
286  template<class T>
287  struct unsigned_or_float<T, enable_if_t<!std::numeric_limits<T>::is_iec559>> : make_unsigned<T> {
288  };
289 
290  template<class T>
291  using unsigned_or_float_t = typename unsigned_or_float<T>::type;
292 
293  template<class Encompasser, class Encompassed, class Enable = void>
294  struct encompasses_lower;
295 
296  template<class Encompasser, class Encompassed>
297  struct encompasses_lower<Encompasser, Encompassed,
298  enable_if_t<std::numeric_limits<Encompasser>::is_signed
299  && std::numeric_limits<Encompassed>::is_signed>> {
300  static constexpr bool value = std::numeric_limits<Encompasser>::lowest()
301  <= std::numeric_limits<Encompassed>::lowest();
302  };
303 
304  template<class Encompasser, class Encompassed>
305  struct encompasses_lower<Encompasser, Encompassed,
306  enable_if_t<!std::numeric_limits<Encompassed>::is_signed>> : std::true_type {
307  };
308 
309  template<class Encompasser, class Encompassed>
310  struct encompasses_lower<Encompasser, Encompassed,
311  enable_if_t<!std::numeric_limits<Encompasser>::is_signed
312  && std::numeric_limits<Encompassed>::is_signed>> : std::false_type {
313  };
314 
315  // true if Encompassed can be cast to Encompasser without chance of overflow
316  template<class Encompasser, class Encompassed>
317  struct encompasses {
318  static constexpr bool _lower = encompasses_lower<Encompasser, Encompassed>::value;
319  static constexpr bool _upper =
320  static_cast<unsigned_or_float_t<Encompasser>>(std::numeric_limits<Encompasser>::max())
321  >= static_cast<unsigned_or_float_t<Encompassed>>(std::numeric_limits<Encompassed>::max());
322 
323  static constexpr bool value = _lower && _upper;
324  };
325 
327  // sg14::_impl::is_integer_or_float - trait to identify 'traditional' arithmetic concept
328 
329  template<class T>
330  struct is_integer_or_float : std::integral_constant<
331  bool,
332  std::numeric_limits<T>::is_integer || std::numeric_limits<T>::is_iec559> {
333  };
334  }
335 
337  // sg14::_impl::to_rep
338 
339  template<class Number, class Enable = void>
340  struct to_rep {
341  constexpr Number operator()(const Number &number) const {
342  // by default, the rep type of a number type is the number type itself
343  return number;
344  }
345  };
346 
347  namespace _impl {
348  template<class Number, class Enable = void>
349  constexpr auto to_rep(const Number &number)
350  -> decltype(sg14::to_rep<Number>()(number)) {
351  return sg14::to_rep<Number>()(number);
352  }
353  }
354 
356  // sg14::from_rep
357 
358  template<class Number, class Enable = void>
359  struct from_rep {
360  template<class Rep>
361  constexpr Number operator()(const Rep &rep) const {
362  // by default, a number type's rep type is the number type itself
363  return static_cast<Number>(rep);
364  }
365  };
366 
367  namespace _impl {
368  template<class Number, class Rep>
369  constexpr auto from_rep(const Rep &rep)
370  -> decltype(sg14::from_rep<Number>()(rep)) {
371  return sg14::from_rep<Number>()(rep);
372  }
373  }
374 
376  // sg14::for_rep
377 
378  // invoke a given generic lambda on given parameters
379  template<class Result, class F, class ... Args,
380  _impl::enable_if_t<!_num_traits_impl::are_composite<Args ...>::value, int> dummy = 0>
381  constexpr Result for_rep(F f, Args &&...args) {
382  return f(std::forward<Args>(args)...);
383  }
384 
385  template<class Result, class F, class ... Args,
386  _impl::enable_if_t<_num_traits_impl::are_composite<Args ...>::value, int> dummy = 0>
387  constexpr Result for_rep(F f, Args &&...args) {
388  return for_rep<Result>(f, _impl::to_rep<typename std::decay<Args>::type>(std::forward<Args>(args))...);
389  }
390 
392  // sg14::from_value
393 
394  // if Number has Value for its Rep, what type would Number become?
395  template<class Number, class Value, class Enable = void>
396  struct from_value;
397 
398  template<class Number, class Value>
399  struct from_value<Number, Value, _impl::enable_if_t<std::is_integral<Number>::value>> {
400  using type = Number;
401  };
402 
403  template<class Number, class Value>
404  using from_value_t = typename from_value<Number, Value>::type;
405 
406  namespace _impl {
407  template<class Number, class Value>
408  constexpr auto from_value(const Value &value)
409  -> sg14::from_value_t<Number, Value> {
410  return value;
411  }
412  }
413 
415  // sg14::scale
416 
417  namespace _num_traits_impl {
418  template<class T>
419  using scale_result_type = decltype(std::declval<T>() * std::declval<T>());
420 
421  template<class T>
422  constexpr scale_result_type<T> pown(int base, int exp) {
423  return exp
424  ? pown<T>(base, exp - 1) * static_cast<scale_result_type<T>>(base)
425  : static_cast<scale_result_type<T>>(1);
426  }
427 
428  template<class T>
429  constexpr scale_result_type<T> pow2(int exp) {
430  return scale_result_type<T>{1} << exp;
431  }
432 
433  template<class T>
434  constexpr scale_result_type<T> pow(int base, int exp) {
435  return (base == 2) ? pow2<T>(exp) : pown<T>(base, exp);
436  }
437  }
438 
439  template<class T>
440  struct scale {
441  constexpr auto operator()(const T &i, int base, int exp) const
442  -> _num_traits_impl::scale_result_type<T> {
443  return _impl::from_rep<_num_traits_impl::scale_result_type<T>>(
444  (exp < 0)
445  ? _impl::to_rep<T>(i) / _num_traits_impl::pow<T>(base, -exp)
446  : _impl::to_rep<T>(i) * _num_traits_impl::pow<T>(base, exp));
447  }
448  };
449 
450  namespace _impl {
451  template<class T>
452  constexpr auto scale(const T &i, int base, int exp)
453  -> decltype(sg14::scale<T>()(i, base, exp)) {
454  return sg14::scale<T>()(i, base, exp);
455  }
456  }
457 }
458 
459 #endif // SG14_NUMERIC_TRAITS
study group 14 of the C++ working group
Definition: const_integer.h:22