fixed_point  rev.2
Binary Fixed-Point Arithmetic Library in C++
fixed_point_type.h
1 
2 // Copyright John McFarlane 2015 - 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_FIXED_POINT_DEF_H)
11 #define SG14_FIXED_POINT_DEF_H 1
12 
13 #include <sg14/auxiliary/const_integer.h>
14 #include <sg14/bits/number_base.h>
15 
17 namespace sg14 {
18  // forward declaration
19  template<class Rep = int, int Exponent = 0>
20  class fixed_point;
21 
23  // implementation-specific definitions
24 
25  namespace _impl {
26  namespace fp {
28  // sg14::_impl::float_of_size
29 
30  template<int NumBits, class Enable = void>
31  struct float_of_size;
32 
33  template<int NumBits>
34  struct float_of_size<NumBits, enable_if_t<NumBits <= sizeof(float)*CHAR_BIT>> {
35  using type = float;
36  };
37 
38  template<int NumBits>
39  struct float_of_size<NumBits, enable_if_t<sizeof(float)*CHAR_BIT < NumBits && NumBits <= sizeof(double)*CHAR_BIT>> {
40  using type = double;
41  };
42 
43  template<int NumBits>
44  struct float_of_size<NumBits, enable_if_t<sizeof(double)*CHAR_BIT < NumBits && NumBits <= sizeof(long double)*CHAR_BIT>> {
45  using type = long double;
46  };
47 
49  // sg14::_impl::float_of_same_size
50 
51  template<class T>
52  using float_of_same_size = typename float_of_size<digits<T>::value + is_signed<T>::value>::type;
53  }
54  }
55 
66 
67  template<class Rep, int Exponent>
68  class fixed_point
69  : public _impl::number_base<fixed_point<Rep, Exponent>, Rep> {
70  using _base = _impl::number_base<fixed_point<Rep, Exponent>, Rep>;
71  public:
73  // types
74 
76  using rep = Rep;
77 
79  // constants
80 
82  constexpr static int exponent = Exponent;
83 
86  constexpr static int digits = std::numeric_limits<Rep>::digits;
87 
90  constexpr static int integer_digits = digits+exponent;
91 
94  constexpr static int fractional_digits = -exponent;
95 
97  // functions
98 
99  private:
100  // constructor taking representation explicitly using operator++(int)-style trick
101  constexpr fixed_point(rep r, int)
102  :_base(r)
103  {
104  }
105 
106  public:
108  constexpr fixed_point() : _base() { }
109 
111  template<class FromRep, int FromExponent>
113  : _base(fixed_point_to_rep(rhs))
114  {
115  }
116 
118  template<class Integral, Integral Constant>
119  constexpr fixed_point(const std::integral_constant<Integral, Constant>&)
120  : fixed_point(fixed_point<Integral, 0>::from_data(Constant))
121  {
122  }
123 
125  template<class S, _impl::enable_if_t<std::numeric_limits<S>::is_integer, int> Dummy = 0>
126  constexpr fixed_point(const S& s)
127  : fixed_point(fixed_point<S, 0>::from_data(s))
128  {
129  }
130 
132  template<class Integral, Integral Value, int Digits>
134  : _base(ci << Exponent)
135  {
136  }
137 
139  template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> Dummy = 0>
140  constexpr fixed_point(S s)
141  :_base(floating_point_to_rep(s))
142  {
143  }
144 
146  template<class S, _impl::enable_if_t<std::numeric_limits<S>::is_integer, int> Dummy = 0>
148  {
149  return operator=(fixed_point<S, 0>::from_data(s));
150  }
151 
153  template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> Dummy = 0>
155  {
156  _base::operator=(floating_point_to_rep(s));
157  return *this;
158  }
159 
161  template<class FromRep, int FromExponent>
163  {
164  _base::operator=(fixed_point_to_rep(rhs));
165  return *this;
166  }
167 
169  constexpr operator bool() const
170  {
171  return static_cast<bool>(_base::data());
172  }
173 
175  template<class S, _impl::enable_if_t<std::numeric_limits<S>::is_integer, int> Dummy = 0>
176  constexpr operator S() const
177  {
178  return rep_to_integral<S>(_base::data());
179  }
180 
182  template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> Dummy = 0>
183  explicit constexpr operator S() const
184  {
185  return rep_to_floating_point<S>(_base::data());
186  }
187 
189  static constexpr fixed_point from_data(rep const & r)
190  {
191  return fixed_point(r, 0);
192  }
193 
194  private:
195  template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> Dummy = 0>
196  static constexpr S one();
197 
198  template<class S, _impl::enable_if_t<std::numeric_limits<S>::is_integer, int> Dummy = 0>
199  static constexpr S one();
200 
201  template<class S>
202  static constexpr S inverse_one();
203 
204  template<class S>
205  static constexpr S rep_to_integral(rep r);
206 
207  template<class S>
208  static constexpr rep floating_point_to_rep(S s);
209 
210  template<class S>
211  static constexpr S rep_to_floating_point(rep r);
212 
213  template<class FromRep, int FromExponent>
214  static constexpr rep fixed_point_to_rep(const fixed_point<FromRep, FromExponent>& rhs);
215  };
216 
218  template<class Rep, int Exponent>
220 
223  template<class Rep, int Exponent>
225 
228  template<class Rep, int Exponent>
230 
233  template<class Rep, int Exponent>
235 
237  // general-purpose implementation-specific definitions
238 
239  namespace _impl {
240 
242  // sg14::_impl::is_fixed_point
243 
244  template<class T>
245  struct is_fixed_point
246  : public std::false_type {
247  };
248 
249  template<class Rep, int Exponent>
250  struct is_fixed_point<fixed_point<Rep, Exponent>>
251  : public std::true_type {
252  };
253 
255  // sg14::_impl::shift_left
256 
257  // performs a shift operation by a fixed number of bits avoiding two pitfalls:
258  // 1) shifting by a negative amount causes undefined behavior
259  // 2) converting between integer types of different sizes can lose significant bits during shift right
260 
261  // Exponent == 0
262  template<int exp, class Output, class Input>
263  constexpr Output shift_left(Input i)
264  {
265  using larger = typename std::conditional<
266  digits<Input>::value<=digits<Output>::value,
267  Output, Input>::type;
268 
269  return (exp>-std::numeric_limits<larger>::digits)
270  ? static_cast<Output>(_impl::scale<larger>(i, 2, exp))
271  : Output{0};
272  }
273 
275  // file-local implementation-specific definitions
276 
277  namespace fp {
278  namespace type {
280  // sg14::_impl::fp::type::pow2
281 
282  // returns given power of 2
283  template<class S, int Exponent, enable_if_t<Exponent==0, int> Dummy = 0>
284  constexpr S pow2()
285  {
286  static_assert(std::is_floating_point<S>::value, "S must be floating-point type");
287  return 1;
288  }
289 
290  template<class S, int Exponent,
291  enable_if_t<!(Exponent<=0) && (Exponent<8), int> Dummy = 0>
292  constexpr S pow2()
293  {
294  static_assert(std::is_floating_point<S>::value, "S must be floating-point type");
295  return pow2<S, Exponent-1>()*S(2);
296  }
297 
298  template<class S, int Exponent, enable_if_t<(Exponent>=8), int> Dummy = 0>
299  constexpr S pow2()
300  {
301  static_assert(std::is_floating_point<S>::value, "S must be floating-point type");
302  return pow2<S, Exponent-8>()*S(256);
303  }
304 
305  template<class S, int Exponent,
306  enable_if_t<!(Exponent>=0) && (Exponent>-8), int> Dummy = 0>
307  constexpr S pow2()
308  {
309  static_assert(std::is_floating_point<S>::value, "S must be floating-point type");
310  return pow2<S, Exponent+1>()*S(.5);
311  }
312 
313  template<class S, int Exponent, enable_if_t<(Exponent<=-8), int> Dummy = 0>
314  constexpr S pow2()
315  {
316  static_assert(std::is_floating_point<S>::value, "S must be floating-point type");
317  return pow2<S, Exponent+8>()*S(.003906250);
318  }
319  }
320  }
321  }
322 
324  // sg14::fixed_point<> member definitions
325 
326  template<class Rep, int Exponent>
327  template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> Dummy>
328  constexpr S fixed_point<Rep, Exponent>::one()
329  {
330  return _impl::fp::type::pow2<S, -exponent>();
331  }
332 
333  template<class Rep, int Exponent>
334  template<class S, _impl::enable_if_t<std::numeric_limits<S>::is_integer, int> Dummy>
335  constexpr S fixed_point<Rep, Exponent>::one()
336  {
338  }
339 
340  template<class Rep, int Exponent>
341  template<class S>
343  {
344  static_assert(std::is_floating_point<S>::value, "S must be floating-point type");
345  return _impl::fp::type::pow2<S, exponent>();
346  }
347 
348  template<class Rep, int Exponent>
349  template<class S>
351  {
352  static_assert(std::numeric_limits<S>::is_integer, "S must be integral type");
353 
354  return _impl::shift_left<exponent, S>(r);
355  }
356 
357  template<class Rep, int Exponent>
358  template<class S>
360  {
361  static_assert(std::is_floating_point<S>::value, "S must be floating-point type");
362  return static_cast<rep>(s*one<S>());
363  }
364 
365  template<class Rep, int Exponent>
366  template<class S>
368  {
369  static_assert(std::is_floating_point<S>::value, "S must be floating-point type");
370  return S(r)*inverse_one<S>();
371  }
372 
373  template<class Rep, int Exponent>
374  template<class FromRep, int FromExponent>
376  {
377  return _impl::shift_left<FromExponent-exponent, rep>(rhs.data());
378  }
379 }
380 
381 #endif // SG14_FIXED_POINT_DEF_H
constexpr fixed_point(const_integer< Integral, Value, Digits, Exponent > ci)
constructor taking an integral_constant type
Definition: fixed_point_type.h:133
constexpr fixed_point(S s)
constructor taking a floating-point type
Definition: fixed_point_type.h:140
fixed_point & operator=(const fixed_point< FromRep, FromExponent > &rhs)
copy assignement operator taking a fixed-point type
Definition: fixed_point_type.h:162
static constexpr fixed_point from_data(rep const &r)
creates an instance given the underlying representation value
Definition: fixed_point_type.h:189
constexpr fixed_point()
default constructor
Definition: fixed_point_type.h:108
constexpr fixed_point(const S &s)
constructor taking an integer type
Definition: fixed_point_type.h:126
constexpr fixed_point(const fixed_point< FromRep, FromExponent > &rhs)
constructor taking a fixed-point type
Definition: fixed_point_type.h:112
a compile-time-only integer type like a std::integral_constant with arithmetic support ...
Definition: const_integer.h:98
fixed_point & operator=(S s)
copy assignment operator taking an integer type
Definition: fixed_point_type.h:147
Rep rep
alias to template parameter, Rep
Definition: fixed_point_type.h:76
literal real number approximation that uses fixed-point arithmetic
Definition: fixed_point_type.h:20
constexpr fixed_point(const std::integral_constant< Integral, Constant > &)
constructor taking an integral_constant type
Definition: fixed_point_type.h:119
study group 14 of the C++ working group
Definition: const_integer.h:22