fixed_point  rev.2
Binary Fixed-Point Arithmetic Library in C++
number_base.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 
7 #if !defined(SG14_number_base_H)
8 #define SG14_number_base_H 1
9 
10 #include <sg14/auxiliary/const_integer.h>
11 #include <sg14/bits/common.h>
12 #include <sg14/num_traits.h>
13 
14 #include <limits>
15 #include <type_traits>
16 
17 namespace sg14 {
18  namespace _impl {
19  template<class Derived, class Rep>
20  class number_base {
21  public:
22  using rep = Rep;
23  using _derived = Derived;
24 
25  number_base() = default;
26 
27  constexpr number_base(const rep& r)
28  : _rep(r) { }
29 
30  template<class T>
31  number_base& operator=(const T& r) {
32  _rep = r;
33  return static_cast<Derived&>(*this);
34  }
35 
36  explicit constexpr operator bool() const
37  {
38  return static_cast<bool>(_rep);
39  }
40 
41  constexpr const rep& data() const
42  {
43  return _rep;
44  }
45 
46  static constexpr Derived from_data(const rep& r)
47  {
48  return Derived(r);
49  }
50 
51  private:
52  rep _rep;
53  };
54 
56  // sg14::_impl::is_class_derived_from_number_base
57 
58  // true iff T's base class is sg14::_impl::number_base;
59  // T must be a class;
60  // used by sg14::_impl::is_derived_from_number_base
61  template<class Derived, class Enable = void>
62  struct is_class_derived_from_number_base : std::false_type {};
63 
64  template<class Derived>
65  struct is_class_derived_from_number_base<
66  Derived,
67  enable_if_t<std::is_base_of<number_base<Derived, typename Derived::rep>, Derived>::value>>
68  : std::true_type {};
69 
71  // sg14::_impl::is_derived_from_number_base
72 
73  // true if T is the Derived parameter of a number_base type
74  template<class T, class Enable = void>
75  struct is_derived_from_number_base : std::false_type {};
76 
77  template<class Derived>
78  struct is_derived_from_number_base<Derived, enable_if_t<std::is_class<Derived>::value>>
79  : is_class_derived_from_number_base<Derived> { };
80 
82  // sg14::_impl::enable_if_precedes
83 
84  template<class Former, class Latter>
85  struct precedes {
86  static constexpr bool value =
87  (std::is_floating_point<Former>::value && !std::is_floating_point<Latter>::value)
88  || (is_derived_from_number_base<Former>::value &&
89  !(is_derived_from_number_base<Latter>::value
90  || std::is_floating_point<Latter>::value));
91  };
92 
94  // sg14::_impl::operate
95 
96  // higher OP number_base<>
97  template<
98  class Operator, class Lhs, class RhsDerived, class RhsRep,
99  enable_if_t <precedes<Lhs, RhsDerived>::value, std::nullptr_t> = nullptr>
100  constexpr auto operate(const Lhs& lhs, const number_base<RhsDerived, RhsRep>& rhs, Operator op)
101  -> decltype(op(lhs, static_cast<Lhs>(static_cast<const RhsDerived&>(rhs))))
102  {
103  return op(lhs, static_cast<Lhs>(static_cast<const RhsDerived&>(rhs)));
104  }
105 
106  // number_base<> OP higher
107  template<
108  class Operator, class LhsDerived, class LhsRep, class Rhs,
109  enable_if_t <precedes<Rhs, LhsDerived>::value, std::nullptr_t> = nullptr>
110  constexpr auto operate(const number_base<LhsDerived, LhsRep>& lhs, const Rhs& rhs, Operator op)
111  -> decltype(op(static_cast<Rhs>(static_cast<const LhsDerived&>(lhs)), rhs))
112  {
113  return op(static_cast<Rhs>(static_cast<const LhsDerived&>(lhs)), rhs);
114  }
115 
116  // lower OP number_base<>
117  template<
118  class Operator, class Lhs, class RhsDerived, class RhsRep,
119  enable_if_t <precedes<RhsDerived, Lhs>::value, std::nullptr_t> = nullptr>
120  constexpr auto operate(const Lhs& lhs, const number_base<RhsDerived, RhsRep>& rhs, Operator op)
121  -> decltype(op(_impl::from_value<RhsDerived>(lhs), static_cast<const RhsDerived&>(rhs))) {
122  return op(from_value<RhsDerived>(lhs), static_cast<const RhsDerived&>(rhs));
123  }
124 
125  // number_base<> OP lower
126  template<
127  class Operator, class LhsDerived, class LhsRep, class Rhs,
128  enable_if_t <precedes<LhsDerived, Rhs>::value, std::nullptr_t> = nullptr>
129  constexpr auto operate(const number_base<LhsDerived, LhsRep>& lhs, const Rhs& rhs, Operator op)
130  -> decltype(op(static_cast<const LhsDerived &>(lhs), from_value<LhsDerived>(rhs)))
131  {
132  return op(static_cast<const LhsDerived &>(lhs), from_value<LhsDerived>(rhs));
133  }
134 
135  // unary operate
136  template<class Operator, class RhsDerived, class RhsRep>
137  constexpr auto operate(const number_base<RhsDerived, RhsRep>& rhs, Operator op)
138  -> decltype(op(rhs.data()))
139  {
140  return op(rhs.data());
141  }
142 
144  // sg14::_impl::number_base operators
145 
146  // compound assignment
147 
148  template<class Lhs, class Rhs, class = enable_if_t <is_derived_from_number_base<Lhs>::value>>
149  auto operator+=(Lhs& lhs, const Rhs& rhs)
150  -> decltype(lhs = lhs + rhs)
151  {
152  return lhs = lhs + rhs;
153  }
154 
155  template<class Lhs, class Rhs, class = enable_if_t <is_derived_from_number_base<Lhs>::value>>
156  auto operator-=(Lhs& lhs, const Rhs& rhs)
157  -> decltype(lhs = lhs - rhs)
158  {
159  return lhs = lhs - rhs;
160  }
161 
162  template<class Lhs, class Rhs, class = enable_if_t <is_derived_from_number_base<Lhs>::value>>
163  auto operator*=(Lhs& lhs, const Rhs& rhs)
164  -> decltype(lhs = lhs * rhs)
165  {
166  return lhs = lhs * rhs;
167  }
168 
169  template<class Lhs, class Rhs, class = enable_if_t <is_derived_from_number_base<Lhs>::value>>
170  auto operator/=(Lhs& lhs, const Rhs& rhs)
171  -> decltype(lhs = lhs / rhs)
172  {
173  return lhs = lhs / rhs;
174  }
175 
176  // unary operators
177 
178  template<class RhsDerived, class RhsRep>
179  constexpr auto operator+(const number_base<RhsDerived, RhsRep>& rhs)
180  -> decltype(operate(rhs, plus_tag))
181  {
182  return operate(rhs, plus_tag);
183  }
184 
185  template<class RhsDerived, class RhsRep>
186  constexpr auto operator-(const number_base<RhsDerived, RhsRep>& rhs)
187  -> decltype(operate(rhs, minus_tag))
188  {
189  return operate(rhs, minus_tag);
190  }
191 
192  // binary arithmetic operators
193 
194  template<class Lhs, class Rhs>
195  constexpr auto operator+(const Lhs& lhs, const Rhs& rhs)
196  -> decltype(operate(lhs, rhs, add_tag))
197  {
198  return operate(lhs, rhs, add_tag);
199  }
200 
201  template<class Lhs, class Rhs>
202  constexpr auto operator-(const Lhs& lhs, const Rhs& rhs)
203  -> decltype(operate(lhs, rhs, subtract_tag))
204  {
205  return operate(lhs, rhs, subtract_tag);
206  }
207 
208  template<class Lhs, class Rhs>
209  constexpr auto operator*(const Lhs& lhs, const Rhs& rhs)
210  -> decltype(operate(lhs, rhs, multiply_tag))
211  {
212  return operate(lhs, rhs, multiply_tag);
213  }
214 
215  template<class Lhs, class Rhs>
216  constexpr auto operator/(const Lhs& lhs, const Rhs& rhs)
217  -> decltype(operate(lhs, rhs, divide_tag))
218  {
219  return operate(lhs, rhs, divide_tag);
220  }
221 
222  // binary bitwise logic operators
223 
224  template<class Lhs, class Rhs>
225  constexpr auto operator|(const Lhs& lhs, const Rhs& rhs)
226  -> decltype(operate(lhs, rhs, bitwise_or_tag))
227  {
228  return operate(lhs, rhs, bitwise_or_tag);
229  }
230 
231  template<class Lhs, class Rhs>
232  constexpr auto operator&(const Lhs& lhs, const Rhs& rhs)
233  -> decltype(operate(lhs, rhs, bitwise_and_tag))
234  {
235  return operate(lhs, rhs, bitwise_and_tag);
236  }
237 
238  template<class Lhs, class Rhs>
239  constexpr auto operator^(const Lhs& lhs, const Rhs& rhs)
240  -> decltype(operate(lhs, rhs, bitwise_xor_tag))
241  {
242  return operate(lhs, rhs, bitwise_xor_tag);
243  }
244 
245  // comparison operator
246 
247  template<class Lhs, class Rhs>
248  constexpr auto operator==(const Lhs& lhs, const Rhs& rhs)
249  -> decltype(operate(lhs, rhs, equal_tag))
250  {
251  return operate(lhs, rhs, equal_tag);
252  }
253 
254  template<class Lhs, class Rhs>
255  constexpr auto operator!=(const Lhs& lhs, const Rhs& rhs)
256  -> decltype(operate(lhs, rhs, not_equal_tag))
257  {
258  return operate(lhs, rhs, not_equal_tag);
259  }
260 
261  template<class Lhs, class Rhs>
262  constexpr auto operator<(const Lhs& lhs, const Rhs& rhs)
263  -> decltype(operate(lhs, rhs, less_than_tag))
264  {
265  return operate(lhs, rhs, less_than_tag);
266  }
267 
268  template<class Lhs, class Rhs>
269  constexpr auto operator>(const Lhs& lhs, const Rhs& rhs)
270  -> decltype(operate(lhs, rhs, greater_than_tag))
271  {
272  return operate(lhs, rhs, greater_than_tag);
273  }
274 
275  template<class Lhs, class Rhs>
276  constexpr auto operator<=(const Lhs& lhs, const Rhs& rhs)
277  -> decltype(operate(lhs, rhs, less_than_or_equal_tag))
278  {
279  return operate(lhs, rhs, less_than_or_equal_tag);
280  }
281 
282  template<class Lhs, class Rhs>
283  constexpr auto operator>=(const Lhs& lhs, const Rhs& rhs)
284  -> decltype(operate(lhs, rhs, greater_than_or_equal_tag))
285  {
286  return operate(lhs, rhs, greater_than_or_equal_tag);
287  }
288  }
289 
291  // _impl::number_base<> numeric traits
292 
293  template<class Number>
294  struct is_composite<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> : std::true_type {
295  };
296 
297  namespace _impl {
298  template<class Number>
299  struct get_rep;
300 
301  template<class Number>
302  using get_rep_t = typename get_rep<Number>::type;
303 
304  // given a Number type and an alternative Rep type, make a new Number type
305  // e.g. set_rep_t<fixed_point<int64_t, 42>, uint8_t> --> fixed_point<uint8_t, 42>
306  template<class Number, class NewRep, class Enable = void>
307  struct set_rep;
308 
309  template<class Number, class NewRep>
310  using set_rep_t = typename set_rep<Number, NewRep>::type;
311  }
312 
313  template<class Number>
314  struct make_signed<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> {
315  using type = _impl::set_rep_t<Number, make_signed_t<_impl::get_rep_t<Number>>>;
316  };
317 
318  template<class Number>
319  struct make_unsigned<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> {
320  using type = _impl::set_rep_t<Number, make_unsigned_t<_impl::get_rep_t<Number>>>;
321  };
322 
323  template<class Number>
324  struct from_rep<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> {
325  template<class Rep>
326  constexpr auto operator()(const Rep &rep) const -> Number {
327  return Number::from_data(static_cast<typename Number::rep>(rep));
328  }
329  };
330 
331  template<class Number>
332  struct to_rep<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> {
333  constexpr auto operator()(const typename Number::_derived& number) const
334  -> decltype(number.data()){
335  return number.data();
336  }
337  };
338 
339  template<class Derived, class Rep>
340  struct scale<_impl::number_base<Derived, Rep>> {
341  template<class Input>
342  constexpr Rep operator()(const Input &i, int base, int exp) const {
343  return (exp < 0)
344  ? _impl::to_rep(i) / _num_traits_impl::pow<Rep>(base, -exp)
345  : _impl::to_rep(i) * _num_traits_impl::pow<Rep>(base, exp);
346  }
347  };
348 }
349 
350 namespace std {
352  // std::numeric_limits for sg14::_impl::numeric_limits
353 
354  template<class Derived, class Rep>
355  struct numeric_limits<sg14::_impl::number_base<Derived, Rep>>
356  : numeric_limits<Rep> {
357  // fixed-point-specific helpers
358  using _value_type = Derived;
359  using _rep = typename _value_type::rep;
360  using _rep_numeric_limits = numeric_limits<_rep>;
361 
362  // standard members
363 
364  static constexpr _value_type min() noexcept
365  {
366  return _value_type::from_data(_rep_numeric_limits::min());
367  }
368 
369  static constexpr _value_type max() noexcept
370  {
371  return _value_type::from_data(_rep_numeric_limits::max());
372  }
373 
374  static constexpr _value_type lowest() noexcept
375  {
376  return _value_type::from_data(_rep_numeric_limits::lowest());
377  }
378 
379  static constexpr _value_type epsilon() noexcept
380  {
381  return _value_type::from_data(_rep_numeric_limits::round_error());
382  }
383 
384  static constexpr _value_type round_error() noexcept
385  {
386  return static_cast<_value_type>(_rep_numeric_limits::round_error());
387  }
388 
389  static constexpr _value_type infinity() noexcept
390  {
391  return static_cast<_value_type>(_rep_numeric_limits::infinity());
392  }
393 
394  static constexpr _value_type quiet_NaN() noexcept
395  {
396  return static_cast<_value_type>(_rep_numeric_limits::quiet_NaN());
397  }
398 
399  static constexpr _value_type signaling_NaN() noexcept
400  {
401  return static_cast<_value_type>(_rep_numeric_limits::signaling_NaN());
402  }
403 
404  static constexpr _value_type denorm_min() noexcept
405  {
406  return static_cast<_value_type>(_rep_numeric_limits::denorm_min());
407  }
408  };
409 }
410 
411 #endif // SG14_NUMBER_BASE_H
STL namespace.
study group 14 of the C++ working group
Definition: const_integer.h:22