fixed_point (deprecated)  rev.2
Binary Fixed-Point Arithmetic Library in C++
safe_integer.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_SAFE_INTEGER_H)
11 #define SG14_SAFE_INTEGER_H 1
12 
13 #include <sg14/bits/number_base.h>
14 #include <sg14/fixed_point>
15 #include "overflow.h"
16 
18 namespace sg14 {
20  // macros
21 
22 #define SG14_INTEGER_BIT_SHIFT_DEFINE(OP) \
23  template <class LhsRep, class LhsOverflowTag, class RhsRep, class RhsOverflowTag> \
24  constexpr auto operator OP (const safe_integer<LhsRep, LhsOverflowTag>& lhs, const safe_integer<RhsRep, RhsOverflowTag>& rhs) \
25  -> safe_integer<LhsRep, LhsOverflowTag> { \
26  return lhs.data() OP rhs.data(); } \
27  \
28  template <class Lhs, class RhsRep, class RhsOverflowTag, _impl::enable_if_t<std::is_fundamental<Lhs>::value, int> dummy = 0> \
29  constexpr auto operator OP (const Lhs& lhs, const safe_integer<RhsRep, RhsOverflowTag>& rhs) \
30  -> Lhs { \
31  return lhs OP rhs.data(); } \
32  \
33  template <class LhsRep, class LhsOverflowTag, class Rhs, _impl::enable_if_t<std::is_fundamental<Rhs>::value, int> dummy = 0> \
34  constexpr auto operator OP (const safe_integer<LhsRep, LhsOverflowTag>& lhs, const Rhs& rhs) \
35  -> safe_integer<LhsRep, LhsOverflowTag> { \
36  return safe_integer<LhsRep, LhsOverflowTag>(lhs.data() OP rhs); }
37 
39  // forward-declarations
40 
41  template<class Rep, class OverflowTag>
42  class safe_integer;
43 
44  namespace _integer_impl {
46  // sg14::_integer_impl::is_safe_integer - trait to identify sg14::safe_integer<>
47 
48  template<class T>
49  struct is_safe_integer
50  : std::false_type {
51  };
52 
53  template<class Rep, class OverflowTag>
54  struct is_safe_integer<safe_integer<Rep, OverflowTag>>
55  : std::true_type {
56  };
57 
59  // sg14::_integer_impl::are_integer_class_operands - basically identifies
60  // operands that should go into a function defined here; filters out fixed-point
61 
62  template<class Lhs, class Rhs>
63  struct are_integer_class_operands {
64  static constexpr int integer_class = is_safe_integer<Lhs>::value + is_safe_integer<Rhs>::value;
65  static constexpr int integer_or_float = _impl::is_integer_or_float<Lhs>::value + _impl::is_integer_or_float<Rhs>::value;
66  static constexpr bool value = (integer_class >= 1) && (integer_or_float == 2);
67  };
68 
70  // sg14::_integer_impl::common_type
71 
72  template<class, class, class = void>
73  struct common_type;
74 
75  // given two safe_integer<>, produces the type that is best suited to both of them
76  template<class LhsRep, class RhsRep, class OverflowTag>
77  struct common_type<
78  safe_integer<LhsRep, OverflowTag>,
79  safe_integer<RhsRep, OverflowTag>> {
80  using type = safe_integer<
81  typename std::common_type<LhsRep, RhsRep>::type,
82  OverflowTag>;
83  };
84 
85  // given a safe_integer<> and a built-in integer type,
86  // generates a safe_integer<> type that is as big as both of them (or as close as possible)
87  template<class LhsRep, class LhsOverflowTag, class RhsInteger>
88  struct common_type<
89  safe_integer<LhsRep, LhsOverflowTag>, RhsInteger,
90  _impl::enable_if_t<
91  !_integer_impl::is_safe_integer<RhsInteger>::value && std::is_integral<RhsInteger>::value>> {
92  using type = typename sg14::safe_integer<typename std::common_type<LhsRep, RhsInteger>::type, LhsOverflowTag>;
93  };
94 
95  // given a safe_integer<> and a floating-point type,
96  // generates a floating-point type that is as big as both of them (or as close as possible)
97  template<class LhsRep, class LhsOverflowTag, class Float>
98  struct common_type<
99  safe_integer<LhsRep, LhsOverflowTag>, Float,
100  _impl::enable_if_t<std::is_floating_point<Float>::value>> {
101  using type = typename std::common_type<LhsRep, Float>::type;
102  };
103 
104  // when first type is not safe_integer<> and second type is, reverse the order
105  template<class Lhs, class RhsRep, class RhsOverflowTag>
106  struct common_type<Lhs, safe_integer<RhsRep, RhsOverflowTag>>
107  : common_type<safe_integer<RhsRep, RhsOverflowTag>, Lhs> {
108  };
109  }
110 
112  // sg14::safe_integer<>
113 
114  // an integer which can be customized to react in different ways to overflow;
115  // currently doesn't correctly detect overflow from operators
116  template<class Rep = int, class OverflowTag = throwing_overflow_tag>
117  class safe_integer : public _impl::number_base<safe_integer<Rep, OverflowTag>, Rep> {
118  using _base = _impl::number_base<safe_integer<Rep, OverflowTag>, Rep>;
119  public:
121  // types
122 
123  using rep = Rep;
124  using overflow_tag = OverflowTag;
125 
127  // functions
128 
129  constexpr safe_integer() = delete;
130 
131  template<class RhsRep, class RhsOverflowTag>
132  constexpr safe_integer(const safe_integer<RhsRep, RhsOverflowTag>& rhs)
133  :safe_integer(rhs.data())
134  {
135  }
136 
137  template<class Rhs, _impl::enable_if_t<!_integer_impl::is_safe_integer<Rhs>::value, int> dummy = 0>
138  constexpr safe_integer(const Rhs& rhs)
139  :_base(convert<rep>(overflow_tag{}, rhs))
140  {
141  }
142 
144  template<class Integral, Integral Value, int Digits, int Exponent>
145  constexpr safe_integer(const_integer<Integral, Value, Digits, Exponent>)
146  : _base(static_cast<rep>(Value))
147  {
148  static_assert(Value <= std::numeric_limits<rep>::max(), "initialization by out-of-range value");
149  static_assert(!std::numeric_limits<Integral>::is_signed || Value >= std::numeric_limits<rep>::lowest(), "initialization by out-of-range value");
150  }
151 
152  template<class T>
153  constexpr explicit operator T() const
154  {
155  return static_cast<T>(_base::data());
156  }
157  };
158 
160  // sg14::_impl::set_rep<safe_integer<>>
161 
162  namespace _impl {
163  template<class Rep, class OverflowTag>
164  struct get_rep<safe_integer<Rep, OverflowTag>> {
165  using type = Rep;
166  };
167 
168  template<class OldRep, class OverflowTag, class NewRep>
169  struct set_rep<safe_integer<OldRep, OverflowTag>, NewRep> {
170  using type = safe_integer<NewRep, OverflowTag>;
171  };
172  }
173 
175  // numeric traits
176 
177  template<class Rep, class OverflowTag>
178  struct digits<safe_integer<Rep, OverflowTag>> : digits<Rep> {
179  };
180 
181  template<class Rep, class OverflowTag, _digits_type MinNumBits>
182  struct set_digits<safe_integer<Rep, OverflowTag>, MinNumBits> {
183  using type = safe_integer<set_digits_t<Rep, MinNumBits>, OverflowTag>;
184  };
185 
186  template<class Rep, class OverflowTag, class Value>
187  struct from_value<safe_integer<Rep, OverflowTag>, Value> {
188  using type = safe_integer<Value, OverflowTag>;
189  };
190 
191  template<class Rep, class OverflowTag>
192  struct scale<safe_integer<Rep, OverflowTag>> {
193  using value_type = safe_integer<Rep, OverflowTag>;
194  constexpr auto operator()(const value_type &i, int base, int exp) const
195  -> decltype(_impl::to_rep(i) * _num_traits_impl::pow<value_type>(base, exp)) {
196  return (exp < 0)
197  ? _impl::to_rep(i) / _num_traits_impl::pow<value_type>(base, -exp)
198  : _impl::to_rep(i) * _num_traits_impl::pow<value_type>(base, exp);
199  }
200  };
201 
203  // sg14::make_safe_integer
204 
205  template<class OverflowTag, class Rep>
206  constexpr auto make_safe_integer(Rep const& value)
207  -> safe_integer<Rep, OverflowTag>
208  {
209  return value;
210  }
211 
213  // binary arithmetic
214 
215  namespace _impl {
217  // arithmetc
218 
219  // for arithmetic operands with a common overflow tag
220  template<class OverflowTag, class OperatorTag, class LhsRep, class RhsRep, class = enable_if_t<OperatorTag::is_arithmetic>>
221  constexpr auto operate_common_tag(
222  OverflowTag,
223  OperatorTag,
224  const safe_integer<LhsRep, OverflowTag>& lhs,
225  const safe_integer<RhsRep, OverflowTag>& rhs)
226  -> decltype(make_safe_integer<OverflowTag>(_overflow_impl::operate<OverflowTag, OperatorTag>()(lhs.data(), rhs.data())))
227  {
228  return make_safe_integer<OverflowTag>(_overflow_impl::operate<OverflowTag, OperatorTag>()(lhs.data(), rhs.data()));
229  }
230 
231  // for comparison operands with a common overflow tag
232  template<class OverflowTag, class OperatorTag, class LhsRep, class RhsRep, class = enable_if_t<OperatorTag::is_comparison>>
233  constexpr auto operate_common_tag(
234  OverflowTag,
235  OperatorTag,
236  const safe_integer<LhsRep, OverflowTag>& lhs,
237  const safe_integer<RhsRep, OverflowTag>& rhs)
238  -> decltype(_overflow_impl::operate<OverflowTag, OperatorTag>()(lhs.data(), rhs.data()))
239  {
240  return _overflow_impl::operate<OverflowTag, OperatorTag>()(lhs.data(), rhs.data());
241  }
242 
243  // for arithmetic operands with different policies
244  template<class OperatorTag, class LhsRep, class LhsTag, class RhsRep, class RhsTag>
245  constexpr auto operate(
246  const safe_integer<LhsRep, LhsTag>& lhs,
247  const safe_integer<RhsRep, RhsTag>& rhs,
248  OperatorTag operator_tag)
249  -> decltype(operate_common_tag(common_type_t<LhsTag, RhsTag>{}, operator_tag, lhs, rhs))
250  {
251  return operate_common_tag(common_type_t<LhsTag, RhsTag>{}, operator_tag, lhs, rhs);
252  }
253  }
254 
256  // binary bit-wise
257 
258  SG14_INTEGER_BIT_SHIFT_DEFINE(>>);
259 
260  SG14_INTEGER_BIT_SHIFT_DEFINE(<<);
261 }
262 
263 namespace std {
264  // std::common_type<T, sg14::safe_integer>
265  template<
266  class Lhs,
267  class RhsRep, class RhsOverflowTag>
268  struct common_type<
269  Lhs,
270  sg14::safe_integer<RhsRep, RhsOverflowTag>>
271  : sg14::_integer_impl::common_type<
272  Lhs,
273  sg14::safe_integer<RhsRep, RhsOverflowTag>> {
274  };
275 
276  // std::common_type<sg14::safe_integer, T>
277  template<
278  class LhsRep, class LhsOverflowTag,
279  class Rhs>
280  struct common_type<
281  sg14::safe_integer<LhsRep, LhsOverflowTag>,
282  Rhs>
283  : sg14::_integer_impl::common_type<
284  sg14::safe_integer<LhsRep, LhsOverflowTag>,
285  Rhs> {
286  };
287 
288  // std::common_type<sg14::safe_integer, sg14::fixed_point>
289  template<
290  class LhsRep, class LhsOverflowTag,
291  class RhsRep, int RhsExponent>
292  struct common_type<
293  sg14::safe_integer<LhsRep, LhsOverflowTag>,
294  sg14::fixed_point<RhsRep, RhsExponent>>
295  : std::common_type<
296  sg14::fixed_point<sg14::safe_integer<LhsRep, LhsOverflowTag>, 0>,
297  sg14::fixed_point<RhsRep, RhsExponent>> {
298  };
299 
300  // std::common_type<sg14::fixed_point, sg14::safe_integer>
301  template<
302  class LhsRep, int LhsExponent,
303  class RhsRep, class RhsOverflowTag>
304  struct common_type<
305  sg14::fixed_point<LhsRep, LhsExponent>,
306  sg14::safe_integer<RhsRep, RhsOverflowTag>>
307  : std::common_type<
308  sg14::fixed_point<LhsRep, LhsExponent>,
309  sg14::fixed_point<sg14::safe_integer<RhsRep, RhsOverflowTag>, 0>> {
310  };
311 
312  // std::common_type<sg14::safe_integer, sg14::safe_integer>
313  template<
314  class LhsRep, class LhsOverflowTag,
315  class RhsRep, class RhsOverflowTag>
316  struct common_type<
317  sg14::safe_integer<LhsRep, LhsOverflowTag>,
318  sg14::safe_integer<RhsRep, RhsOverflowTag>>
319  : sg14::_integer_impl::common_type<
320  sg14::safe_integer<LhsRep, LhsOverflowTag>,
321  sg14::safe_integer<RhsRep, RhsOverflowTag>> {
322  };
323 
325  // std::numeric_limits specialization for safe_integer
326 
327  template<class Rep, class OverflowTag>
328  struct numeric_limits<sg14::safe_integer<Rep, OverflowTag>>
329  : numeric_limits<sg14::_impl::number_base<sg14::safe_integer<Rep, OverflowTag>, Rep>> {};
330 }
331 
332 #endif // SG14_SAFE_INTEGER_H
STL namespace.
literal real number approximation that uses fixed-point arithmetic
Definition: fixed_point_type.h:20
study group 14 of the C++ working group
Definition: const_integer.h:22