fixed_point (deprecated)  rev.2
Binary Fixed-Point Arithmetic Library in C++
overflow.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 #ifndef SG14_OVERFLOW_H
11 #define SG14_OVERFLOW_H
12 
13 #include <sg14/num_traits.h>
14 #include <sg14/bits/common.h>
15 
16 #if defined(SG14_EXCEPTIONS_ENABLED)
17 #include <stdexcept>
18 #else
19 #include <cstdio>
20 #include <exception>
21 #endif
22 
24 namespace sg14 {
26  // mode tags and objects
27 
28  // match the behavior of fundamental arithmetic types
29  static constexpr struct native_overflow_tag {
30  } native_overflow{};
31 
32  // throw an exception when overflow is detected
33  static constexpr struct throwing_overflow_tag {
34  } throwing_overflow{};
35 
36  // confine range of results
37  static constexpr struct saturated_overflow_tag {
38  } saturated_overflow{};
39 
41  // sg14::convert
42 
43  // implementation details
44  namespace _overflow_impl {
46  // throw exception (or crash horribly)
47 
48 #if defined(SG14_EXCEPTIONS_ENABLED)
49  template<class Result>
50  constexpr Result return_if(bool condition, const Result& value, const char* )
51  {
52  return condition ? value : throw std::overflow_error("");
53  }
54 #else
55 #if (__cplusplus>=201402L)
56  template<class Result>
57  [[noreturn]] constexpr Result terminate(const char* message) noexcept {
58  std::fputs(message, stderr);
59  std::terminate();
60  return Result{0};
61  }
62 #else
63  template<class Result>
64  [[noreturn]] Result terminate(const char* message) noexcept {
65  std::fputs(message, stderr);
66  std::terminate();
67  return Result{0};
68  }
69 #endif
70 
71  template<class Result>
72  constexpr Result return_if(bool condition, const Result &value, const char* message) {
73  return condition ? value : terminate<Result>(message);
74  }
75 #endif
76 
78  // overflow detection
79 
80  // positive_digits
81  template<class T>
82  struct positive_digits : public std::integral_constant<int, std::numeric_limits<T>::digits> {
83  };
84 
85  template<class T>
86  struct negative_digits
87  : public std::integral_constant<int, std::is_signed<T>::value ? std::numeric_limits<T>::digits : 0> {
88  };
89 
90  // is_positive_overflow
91  template<
92  class Destination, class Source,
93  _impl::enable_if_t<!(positive_digits<Destination>::value<positive_digits<Source>::value), int> dummy = 0>
94  constexpr bool is_positive_overflow(Source const&)
95  {
96  // If positive capacity of Destination is equal to or exceeds that of Source,
97  // positive overflow cannot occur.
98  return false;
99  }
100 
101  template<
102  class Destination, class Source,
103  _impl::enable_if_t<(positive_digits<Destination>::value<positive_digits<Source>::value), int> dummy = 0>
104  constexpr bool is_positive_overflow(Source const& source)
105  {
106  return source>static_cast<Source>(std::numeric_limits<Destination>::max());
107  }
108 
109  // is_negative_overflow
110  template<
111  class Destination, class Source,
112  _impl::enable_if_t<!(negative_digits<Destination>::value<negative_digits<Source>::value), int> dummy = 0>
113  constexpr bool is_negative_overflow(Source const&)
114  {
115  // If positive capacity of Destination is equal to or exceeds that of Source,
116  // positive overflow cannot occur.
117  return false;
118  }
119 
120  template<
121  class Destination, class Source,
122  _impl::enable_if_t<(negative_digits<Destination>::value<negative_digits<Source>::value), int> dummy = 0>
123  constexpr bool is_negative_overflow(Source const& source)
124  {
125  return source<static_cast<Source>(std::numeric_limits<Destination>::lowest());
126  }
127 
129  // operate
130 
131  template<class OverflowTag, class Operator, class Enable = void>
132  struct operate;
133  }
134 
135  template<class Result, class Input>
136  constexpr Result convert(native_overflow_tag, const Input& rhs)
137  {
138  return static_cast<Result>(rhs);
139  }
140 
141  template<class Result, class Input>
142  constexpr Result convert(throwing_overflow_tag, const Input& rhs)
143  {
144  return _impl::encompasses<Result, Input>::value
145  ? static_cast<Result>(rhs)
146  : _overflow_impl::return_if(
147  !_overflow_impl::is_positive_overflow<Result>(rhs),
148  _overflow_impl::return_if(
149  !_overflow_impl::is_negative_overflow<Result>(rhs),
150  static_cast<Result>(rhs),
151  "negative overflow in conversion"),
152  "positive overflow in conversion");
153  }
154 
155  template<class Result, class Input>
156  constexpr Result convert(saturated_overflow_tag, const Input& rhs)
157  {
158  using numeric_limits = std::numeric_limits<Result>;
159  return !_impl::encompasses<Result, Input>::value
160  ? _overflow_impl::is_positive_overflow<Result>(rhs)
161  ? numeric_limits::max()
162  : _overflow_impl::is_negative_overflow<Result>(rhs)
163  ? numeric_limits::lowest()
164  : static_cast<Result>(rhs)
165  : static_cast<Result>(rhs);
166  }
167 
169  // sg14::add
170 
171  // implementation details
172  namespace _overflow_impl {
173  template<>
174  struct operate<native_overflow_tag, _impl::add_op> {
175  template<class Lhs, class Rhs>
176  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
177  -> decltype(lhs+rhs)
178  {
179  return lhs+rhs;
180  }
181  };
182 
183  template<>
184  struct operate<throwing_overflow_tag, _impl::add_op> {
185  template<class Lhs, class Rhs>
186  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
187  -> decltype(lhs+rhs)
188  {
189  using result_type = decltype(lhs+rhs);
190  using numeric_limits = std::numeric_limits<result_type>;
191  return _overflow_impl::return_if(
192  !((rhs>=_impl::from_rep<Rhs>(0))
193  ? (lhs>numeric_limits::max()-rhs)
194  : (lhs<numeric_limits::lowest()-rhs)),
195  lhs+rhs,
196  "overflow in addition");
197  }
198  };
199 
200  template<>
201  struct operate<saturated_overflow_tag, _impl::add_op> {
202  template<class Lhs, class Rhs>
203  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
204  -> _impl::op_result<_impl::add_op, Lhs, Rhs>
205  {
206  using result_type = decltype(lhs+rhs);
207  using numeric_limits = std::numeric_limits<result_type>;
208  return (rhs>0)
209  ? (lhs>numeric_limits::max()-rhs) ? numeric_limits::max() : lhs+rhs
210  : (lhs<numeric_limits::lowest()-rhs) ? numeric_limits::lowest() : lhs+rhs;
211  }
212  };
213  }
214 
215  template<class OverflowTag, class Lhs, class Rhs>
216  constexpr auto add(OverflowTag, const Lhs& lhs, const Rhs& rhs)
217  -> decltype(lhs+rhs)
218  {
219  return for_rep<decltype(lhs+rhs)>(_overflow_impl::operate<OverflowTag, _impl::add_op>(), lhs, rhs);
220  }
221 
223  // sg14::subtract
224 
225  // implementation details
226  namespace _overflow_impl {
227  template<>
228  struct operate<native_overflow_tag, _impl::subtract_op> {
229  template<class Lhs, class Rhs>
230  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
231  -> decltype(lhs-rhs)
232  {
233  return lhs-rhs;
234  }
235  };
236 
237  template<>
238  struct operate<throwing_overflow_tag, _impl::subtract_op> {
239  template<class Lhs, class Rhs>
240  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
241  -> decltype(lhs-rhs)
242  {
243  using result_type = decltype(lhs-rhs);
244  using numeric_limits = std::numeric_limits<result_type>;
245  return _overflow_impl::return_if(
246  (rhs<_impl::from_rep<Rhs>(0))
247  ? (lhs<=numeric_limits::max()+rhs)
248  : (lhs>=numeric_limits::lowest()+rhs),
249  lhs-rhs,
250  "positive overflow in subtraction");
251  }
252  };
253 
254  template<>
255  struct operate<saturated_overflow_tag, _impl::subtract_op> {
256  template<class Lhs, class Rhs>
257  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
258  -> _impl::op_result<_impl::subtract_op, Lhs, Rhs>
259  {
260  using result_type = decltype(lhs-rhs);
261  using numeric_limits = std::numeric_limits<result_type>;
262  return (rhs<0)
263  ? (lhs>numeric_limits::max()+rhs) ? numeric_limits::max() : lhs-rhs
264  : (lhs<numeric_limits::lowest()+rhs) ? numeric_limits::lowest() : lhs-rhs;
265  }
266  };
267  }
268 
269  template<class OverflowTag, class Lhs, class Rhs>
270  constexpr auto subtract(OverflowTag, const Lhs& lhs, const Rhs& rhs)
271  -> decltype(lhs-rhs)
272  {
273  return for_rep<decltype(lhs-rhs)>(_overflow_impl::operate<OverflowTag, _impl::subtract_op>(), lhs, rhs);
274  }
275 
277  // sg14::multiply
278 
279  // implementation details
280  namespace _overflow_impl {
281  template<>
282  struct operate<native_overflow_tag, _impl::multiply_op> {
283  template<class Lhs, class Rhs>
284  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
285  -> decltype(lhs*rhs)
286  {
287  return lhs*rhs;
288  }
289  };
290 
291  template<class Lhs, class Rhs>
292  constexpr bool is_multiply_overflow(const Lhs& lhs, const Rhs& rhs)
293  {
294  using result_nl = std::numeric_limits<decltype(lhs*rhs)>;
295  return lhs && rhs && ((lhs>Lhs{})
296  ? ((rhs>Rhs{}) ? (result_nl::max()/rhs) : (result_nl::lowest()/rhs))<lhs
297  : ((rhs>Rhs{}) ? (result_nl::lowest()/rhs) : (result_nl::max()/rhs))>lhs);
298  }
299 
300  template<>
301  struct operate<throwing_overflow_tag, _impl::multiply_op> {
302  template<class Lhs, class Rhs>
303  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
304  -> decltype(lhs*rhs)
305  {
306  return _overflow_impl::return_if(
307  !is_multiply_overflow(lhs, rhs),
308  lhs*rhs, "overflow in multiplication");
309  }
310  };
311 
312  template<>
313  struct operate<saturated_overflow_tag, _impl::multiply_op> {
314  template<class Lhs, class Rhs>
315  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
316  -> _impl::op_result<_impl::multiply_op, Lhs, Rhs>
317  {
318  using result_type = decltype(lhs*rhs);
319  return is_multiply_overflow(lhs, rhs)
320  ? ((lhs>0) ^ (rhs>0))
321  ? std::numeric_limits<result_type>::lowest()
322  : std::numeric_limits<result_type>::max()
323  : lhs*rhs;
324  }
325  };
326  }
327 
328  template<class OverflowTag, class Lhs, class Rhs>
329  constexpr auto multiply(OverflowTag, const Lhs& lhs, const Rhs& rhs)
330  -> decltype(lhs*rhs)
331  {
332  return for_rep<decltype(lhs*rhs)>(_overflow_impl::operate<OverflowTag, _impl::multiply_op>(), lhs, rhs);
333  }
334 
336  // sg14::divide
337 
338  // implementation details
339  namespace _overflow_impl {
340  template<class OverflowTag>
341  struct operate<OverflowTag, _impl::divide_op> {
342  template<class Lhs, class Rhs>
343  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
344  -> decltype(lhs/rhs)
345  {
346  return lhs/rhs;
347  }
348  };
349  }
350 
352  // comparison
353 
354  // implementation details
355  namespace _overflow_impl {
356  template<class Operator>
357  struct operate<native_overflow_tag, Operator,
358  _impl::enable_if_t<Operator::is_comparison>> {
359  template<class Lhs, class Rhs>
360  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
361  -> decltype(Operator()(lhs, rhs))
362  {
363  return Operator()(lhs, rhs);
364  }
365  };
366 
367  template<class Operator>
368  struct operate<throwing_overflow_tag, Operator,
369  _impl::enable_if_t<Operator::is_comparison>> {
370  template<class Lhs, class Rhs>
371  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
372  -> _impl::op_result<Operator, Lhs, Rhs>
373  {
374  return Operator()(lhs, rhs);
375  }
376  };
377 
378  template<class Operator>
379  struct operate<saturated_overflow_tag, Operator,
380  _impl::enable_if_t<Operator::is_comparison>> {
381  template<class Lhs, class Rhs>
382  constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
383  -> _impl::op_result<Operator, Lhs, Rhs>
384  {
385  // assumes all arithmetic-induced implicit convertion goes the same
386  // or at least that `|` is a less "promotion-inducing" operation
387  using converted = decltype(lhs | rhs);
388  return Operator()(
389  convert<converted>(saturated_overflow, lhs),
390  convert<converted>(saturated_overflow, rhs));
391  }
392  };
393  }
394 }
395 
396 #endif //SG14_OVERFLOW_H
study group 14 of the C++ working group
Definition: const_integer.h:22