fixed_point (deprecated)  rev.2
Binary Fixed-Point Arithmetic Library in C++
elastic_integer.h
1 
2 // Copyright John McFarlane 2016.
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_ELASTIC_INTEGER_H)
11 #define SG14_ELASTIC_INTEGER_H 1
12 
13 #include <sg14/bits/number_base.h>
14 
16 namespace sg14 {
17 
19  // forward-declaration
20 
21  template<int Digits, class Narrowest>
23 
25  // numeric traits
26 
27  namespace _elastic_integer_impl {
28  template<int Digits, class Narrowest>
29  struct base_class {
30  static constexpr _digits_type digits = Digits;
31 
32  static constexpr _digits_type rep_digits = _impl::max(sg14::digits<Narrowest>::value, digits);
33 
34  using rep = typename set_digits<Narrowest, rep_digits>::type;
35  using type = _impl::number_base<elastic_integer<Digits, Narrowest>, rep>;
36  };
37 
38  template<int Digits, class Narrowest>
39  using base_class_t = typename base_class<Digits, Narrowest>::type;
40  }
41 
42  template<int Digits, class Narrowest>
43  struct digits<elastic_integer<Digits, Narrowest>> : std::integral_constant<_digits_type, Digits> {
44  static constexpr _digits_type value = Digits;
45  };
46 
47  template<int Digits, class Narrowest, _digits_type MinNumBits>
48  struct set_digits<elastic_integer<Digits, Narrowest>, MinNumBits> {
50  };
51 
52  namespace _impl {
53  template<int Digits, class Narrowest>
54  struct get_rep<elastic_integer<Digits, Narrowest>> {
55  using type = Narrowest;
56  };
57 
58  template<int Digits, class OldNarrowest, class NewNarrowest>
59  struct set_rep<elastic_integer<Digits, OldNarrowest>, NewNarrowest> {
61  };
62  }
63 
64  template<int Digits, class Narrowest, class Value>
65  struct from_value<elastic_integer<Digits, Narrowest>, Value> {
66  using type = elastic_integer<sg14::digits<Value>::value, sg14::_impl::make_signed_t<Narrowest, sg14::is_signed<Value>::value>>;
67  };
68 
69  template<int Digits, class Narrowest>
70  struct scale<elastic_integer<Digits, Narrowest>> {
71  using _value_type = elastic_integer<Digits, Narrowest>;
72 
73  constexpr _value_type operator()(const _value_type& i, int base, int exp) const {
74  using _rep = typename _value_type::rep;
75  return _value_type{ _impl::scale(i.data(), base, exp) };
76  }
77  };
78 
89 
90  template<int Digits, class Narrowest = int>
91  class elastic_integer : public _elastic_integer_impl::base_class_t<Digits, Narrowest> {
92  static_assert(Digits > 0, "type requires positive number of digits");
93  using _base = _elastic_integer_impl::base_class_t<Digits, Narrowest>;
94  public:
96  static constexpr int digits = Digits;
97 
99  using narrowest = Narrowest;
100 
102  using rep = typename _base::rep;
103 
105  constexpr elastic_integer() = default;
106 
108  constexpr elastic_integer(const elastic_integer& rhs)
109  :_base(rhs)
110  {
111  }
112 
114  template<class Number, _impl::enable_if_t<std::numeric_limits<Number>::is_specialized, int> Dummy = 0>
115  constexpr elastic_integer(Number n)
116  : _base(static_cast<rep>(n))
117  {
118  }
119 
121  template<int FromWidth, class FromNarrowest>
123  :_base(static_cast<rep>(rhs.data()))
124  {
125  }
126 
128  template<class Integral, Integral Value, int Exponent>
130  : _base(static_cast<rep>(Value))
131  {
132  static_assert(!sg14::is_signed<Integral>::value || sg14::is_signed<rep>::value, "initialization by out-of-range value");
133  }
134 
136  template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> Dummy = 0>
138  {
139  _base::operator=(floating_point_to_rep(s));
140  return *this;
141  }
142 
144  template<class S>
145  explicit constexpr operator S() const
146  {
147  return static_cast<S>(_base::data());
148  }
149  };
150 
152  // sg14::make_elastic_integer
153 
154  template<
155  class Integral, Integral Value>
156  constexpr auto make_elastic_integer(const_integer<Integral, Value>)
158  {
159  return elastic_integer<used_bits(Value)>{Value};
160  }
161 
162  template<class Narrowest = int, class Integral, _impl::enable_if_t<!is_const_integer<Integral>::value, int> Dummy = 0>
163  constexpr auto make_elastic_integer(const Integral& value)
164  -> decltype(elastic_integer<std::numeric_limits<Integral>::digits, Narrowest>{value})
165  {
167  }
168 
169  namespace _elastic_integer_impl {
171  // sg14::_elastic_integer_impl::is_elastic_integer
172 
173  template<class ElasticInteger>
174  struct is_elastic_integer : std::false_type {
175  };
176 
177  template<int Digits, class Narrowest>
178  struct is_elastic_integer<elastic_integer<Digits, Narrowest>> : std::true_type {
179  };
180 
182  // sg14::_elastic_integer_impl::are_integer_class_operands - basically identifies
183  // operands that should go into a function defined here; filters out fixed-point
184 
185  template<class Lhs, class Rhs>
186  struct are_integer_class_operands {
187  static constexpr int integer_class = is_elastic_integer<Lhs>::value+is_elastic_integer<Rhs>::value;
188  static constexpr int integer_or_float =
189  _impl::is_integer_or_float<Lhs>::value+_impl::is_integer_or_float<Rhs>::value;
190  static constexpr bool value = (integer_class>=1) && (integer_or_float==2);
191  };
192  }
193 
195  // bitwise operators
196 
197  // operator~
198  template<int RhsDigits, class RhsNarrowest>
199  constexpr auto operator~(const elastic_integer<RhsDigits, RhsNarrowest>& rhs)
201  {
203  using rep = typename elastic_integer::rep;
204  return elastic_integer::from_data(
205  static_cast<rep>(
206  rhs.data()
207  ^ ((static_cast<rep>(~0)) >> (std::numeric_limits<rep>::digits - RhsDigits))));
208  }
209 
210  // operator<<
211  template<int LhsDigits, class LhsNarrowest, class Rhs>
212  constexpr auto operator<<(const elastic_integer<LhsDigits, LhsNarrowest>& lhs, const Rhs& rhs)
214  {
215  return elastic_integer<LhsDigits, LhsNarrowest>::from_data(lhs.data() << rhs);
216  }
217 
218  template<class Lhs, int RhsDigits, class RhsNarrowest>
219  constexpr auto operator<<(const Lhs& lhs, const elastic_integer<RhsDigits, RhsNarrowest>& rhs)
220  -> decltype(lhs << 0)
221  {
222  return lhs << rhs.data();
223  }
224 
225  // operator>>
226  template<int LhsDigits, class LhsNarrowest, class Rhs>
227  constexpr auto operator>>(const elastic_integer<LhsDigits, LhsNarrowest>& lhs, const Rhs& rhs)
229  {
230  return elastic_integer<LhsDigits, LhsNarrowest>::from_data(lhs.data() >> rhs);
231  }
232 
233  template<class Lhs, int RhsDigits, class RhsNarrowest>
234  constexpr auto operator>>(const Lhs& lhs, const elastic_integer<RhsDigits, RhsNarrowest>& rhs)
235  -> decltype(lhs >> 0)
236  {
237  return lhs >> rhs.data();
238  }
239 
241  // comparison operators
242 
243  namespace _impl {
244  template<int FromDigits, class FromNarrowest, int OtherDigits, class OtherNarrowest,
245  _impl::enable_if_t<FromDigits!=OtherDigits || !std::is_same<FromNarrowest, OtherNarrowest>::value, std::nullptr_t> Dummy = nullptr>
246  constexpr auto cast_to_common_type(
249  -> decltype(static_cast<_impl::common_type_t<
252  return static_cast<_impl::common_type_t<
255  };
256 
257  template<class Operator, int LhsDigits, class LhsNarrowest, int RhsDigits, class RhsNarrowest,
258 #if defined(__GNUG__)
259  bool Enable = Operator::is_comparison>
260 #else
261  enable_if_t<Operator::is_comparison>...>
262 #endif
263  constexpr auto operate(
266  Operator op)
267  -> decltype(op(cast_to_common_type(lhs, rhs), cast_to_common_type(rhs, lhs)))
268  {
269  return op(cast_to_common_type(lhs, rhs), cast_to_common_type(rhs, lhs));
270  }
271 
272  template<class Operator, int Digits, class Narrowest,
273  class = enable_if_t<Operator::is_comparison>>
274  constexpr auto
275  operate(const elastic_integer<Digits, Narrowest>& lhs, const elastic_integer<Digits, Narrowest>& rhs, Operator op)
276  -> decltype(op(lhs.data(), rhs.data()))
277  {
278  return op(lhs.data(), rhs.data());
279  }
280  }
281 
283  // arithmetic operators
284 
285  namespace _impl {
287  // policies
288 
289  template<class Operation, class LhsTraits, class RhsTraits>
290  struct policy;
291 
292  template<class LhsTraits, class RhsTraits>
293  struct policy<_impl::add_op, LhsTraits, RhsTraits> {
294  static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits)+1;
295  static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed;
296  };
297 
298  template<class LhsTraits, class RhsTraits>
299  struct policy<_impl::subtract_op, LhsTraits, RhsTraits> {
300  static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits) + (LhsTraits::is_signed | RhsTraits::is_signed);
301  static constexpr bool is_signed = true;
302  };
303 
304  template<class LhsTraits, class RhsTraits>
305  struct policy<_impl::multiply_op, LhsTraits, RhsTraits> {
306  static constexpr int contribution(int operand_digits) { return operand_digits == 1 ? 0 : operand_digits; }
307  static constexpr int digits = max(1, contribution(LhsTraits::digits)+contribution(RhsTraits::digits));
308  static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed;
309  };
310 
311  template<class LhsTraits, class RhsTraits>
312  struct policy<_impl::divide_op, LhsTraits, RhsTraits> {
313  static constexpr int digits = LhsTraits::digits;
314  static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed;
315  };
316 
317  template<class LhsTraits, class RhsTraits>
318  struct policy<_impl::bitwise_or_op, LhsTraits, RhsTraits> {
319  static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits);
320  static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed;
321  };
322 
323  template<class LhsTraits, class RhsTraits>
324  struct policy<_impl::bitwise_and_op, LhsTraits, RhsTraits> {
325  static constexpr int digits = _impl::min(LhsTraits::digits, RhsTraits::digits);
326  static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed;
327  };
328 
329  template<class LhsTraits, class RhsTraits>
330  struct policy<_impl::bitwise_xor_op, LhsTraits, RhsTraits> {
331  static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits);
332  static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed;
333  };
334 
336  // operate_params
337 
338  template<class OperationTag, int LhsDigits, class LhsNarrowest, int RhsDigits, class RhsNarrowest,
339  class = enable_if_t<OperationTag::is_arithmetic>>
340  struct operate_params {
343  using lhs_traits = std::numeric_limits<lhs>;
344  using rhs_traits = std::numeric_limits<rhs>;
345 
346  using policy = typename _impl::policy<OperationTag, lhs_traits, rhs_traits>;
347 
348  using lhs_rep = typename lhs::rep;
349  using rhs_rep = typename rhs::rep;
350  using rep_result = typename _impl::op_result<OperationTag, lhs_rep, rhs_rep>;
351 
352  static constexpr _digits_type narrowest_width = _impl::max(
353  digits<LhsNarrowest>::value + sg14::is_signed<LhsNarrowest>::value,
354  digits<RhsNarrowest>::value + sg14::is_signed<RhsNarrowest>::value);
355  using narrowest = set_digits_t<_impl::make_signed_t<rep_result, policy::is_signed>, narrowest_width-policy::is_signed>;
356  using result_type = elastic_integer<policy::digits, narrowest>;
357  };
358 
360  // sg14::_impl::operate
361 
362  template<class Operator, int LhsDigits, class LhsNarrowest, int RhsDigits, class RhsNarrowest,
363 #if defined(__GNUG__)
364  bool Enable = Operator::is_arithmetic>
365 #else
366  enable_if_t<Operator::is_arithmetic>...>
367 #endif
368  constexpr auto operate(
371  Operator op)
372  -> typename operate_params<Operator, LhsDigits, LhsNarrowest, RhsDigits, RhsNarrowest>::result_type
373  {
374  using result_type = typename operate_params<Operator, LhsDigits, LhsNarrowest, RhsDigits, RhsNarrowest>::result_type;
375  return result_type::from_data(
376  static_cast<typename result_type::rep>(op(
377  static_cast<result_type>(lhs).data(),
378  static_cast<result_type>(rhs).data())));
379  }
380  }
381 
382  // unary operator-
383  template<int RhsDigits, class RhsNarrowest>
384  constexpr auto operator-(const elastic_integer<RhsDigits, RhsNarrowest>& rhs)
386  {
388  return result_type::from_data(-static_cast<result_type>(rhs).data());
389  }
390 
391  // unary operator+
392  template<int RhsDigits, class RhsNarrowest>
393  constexpr auto operator+(const elastic_integer<RhsDigits, RhsNarrowest>& rhs)
395  {
397  return result_type::from_data(static_cast<result_type>(rhs).data());
398  }
399 }
400 
401 namespace std {
402  template<int LhsDigits, class LhsNarrowest, int RhsDigits, class RhsNarrowest>
403  struct common_type<sg14::elastic_integer<LhsDigits, LhsNarrowest>, sg14::elastic_integer<RhsDigits, RhsNarrowest>> {
404  using type = sg14::elastic_integer<
405  sg14::_impl::max(LhsDigits, RhsDigits),
406  sg14::_impl::common_signedness_t<LhsNarrowest, RhsNarrowest>>;
407  };
408 
409  template<int LhsDigits, class LhsNarrowest, class Rhs>
410  struct common_type<sg14::elastic_integer<LhsDigits, LhsNarrowest>, Rhs>
411  : common_type<sg14::elastic_integer<LhsDigits, LhsNarrowest>, sg14::elastic_integer<std::numeric_limits<Rhs>::digits, Rhs>> {
412  };
413 
414  template<class Lhs, int RhsDigits, class RhsNarrowest>
415  struct common_type<Lhs, sg14::elastic_integer<RhsDigits, RhsNarrowest>>
416  : common_type<sg14::elastic_integer<std::numeric_limits<Lhs>::digits, Lhs>, sg14::elastic_integer<RhsDigits, RhsNarrowest>> {
417  };
418 
420  // std::numeric_limits for sg14::elastic_integer
421 
422  namespace _elastic_integer_impl {
424  // sg14::_elastic_integer_impl::lowest
425 
426  // helper for std::numeric_limits<sg14::elastic_integer<>>::lowest()
427  template<class Rep, bool IsSigned>
428  struct lowest;
429 
430  template<class Rep>
431  struct lowest<Rep, true> {
432  constexpr Rep operator()(const Rep& max) const noexcept
433  {
434  return -max;
435  }
436  };
437 
438  template<class Rep>
439  struct lowest<Rep, false> {
440  constexpr Rep operator()(const Rep&) const noexcept
441  {
442  return 0;
443  }
444  };
445  };
446 
447  template<int Digits, class Narrowest>
448  struct numeric_limits<sg14::elastic_integer<Digits, Narrowest>>
449  : numeric_limits<Narrowest> {
450  // elastic integer-specific helpers
451  using _narrowest_numeric_limits = numeric_limits<Narrowest>;
452  using _value_type = sg14::elastic_integer<Digits, Narrowest>;
453  using _rep = typename _value_type::rep;
454  using _rep_numeric_limits = numeric_limits<_rep>;
455 
456  static constexpr _rep _rep_max() noexcept
457  {
458  return _rep_numeric_limits::max() >> (_rep_numeric_limits::digits-digits);
459  }
460 
461  // standard members
462  static constexpr int digits = Digits;
463 
464  static constexpr _value_type min() noexcept
465  {
466  return _value_type::from_data(1);
467  }
468 
469  static constexpr _value_type max() noexcept
470  {
471  return _value_type::from_data(_rep_max());
472  }
473 
474  static constexpr _value_type lowest() noexcept
475  {
476  return _elastic_integer_impl::lowest<_rep, _narrowest_numeric_limits::is_signed>()(_rep_max());
477  }
478  };
479 }
480 
481 #endif // SG14_ELASTIC_INTEGER_H
constexpr elastic_integer(const_integer< Integral, Value, Digits, Exponent >)
constructor taking an integral constant
Definition: elastic_integer.h:129
literal integer type that encodes its width in bits within its type
Definition: elastic_integer.h:22
STL namespace.
constexpr elastic_integer(const elastic_integer< FromWidth, FromNarrowest > &rhs)
constructor taking an elastic_integer type
Definition: elastic_integer.h:122
elastic_integer & operator=(S s)
copy assignment operator taking a floating-point type
Definition: elastic_integer.h:137
a compile-time-only integer type like a std::integral_constant with arithmetic support ...
Definition: const_integer.h:98
constexpr elastic_integer(Number n)
construct from numeric type
Definition: elastic_integer.h:115
typename _base::rep rep
the actual type used to store the value; closely related to Narrowest but may be a different width ...
Definition: elastic_integer.h:102
constexpr elastic_integer(const elastic_integer &rhs)
common copy constructor
Definition: elastic_integer.h:108
Narrowest narrowest
alias to template parameter, Narrowest
Definition: elastic_integer.h:99
study group 14 of the C++ working group
Definition: const_integer.h:22