10 #ifndef SG14_OVERFLOW_H 11 #define SG14_OVERFLOW_H 13 #include <sg14/num_traits.h> 14 #include <sg14/bits/common.h> 16 #if defined(SG14_EXCEPTIONS_ENABLED) 29 static constexpr
struct native_overflow_tag {
33 static constexpr
struct throwing_overflow_tag {
34 } throwing_overflow{};
37 static constexpr
struct saturated_overflow_tag {
38 } saturated_overflow{};
44 namespace _overflow_impl {
48 #if defined(SG14_EXCEPTIONS_ENABLED) 49 template<
class Result>
50 constexpr Result return_if(
bool condition,
const Result& value,
const char* )
52 return condition ? value :
throw std::overflow_error(
"");
55 #if (__cplusplus>=201402L) 56 template<
class Result>
57 [[noreturn]] constexpr Result terminate(
const char* message) noexcept {
58 std::fputs(message, stderr);
63 template<
class Result>
64 [[noreturn]] Result terminate(
const char* message) noexcept {
65 std::fputs(message, stderr);
71 template<
class Result>
72 constexpr Result return_if(
bool condition,
const Result &value,
const char* message) {
73 return condition ? value : terminate<Result>(message);
82 struct positive_digits :
public std::integral_constant<int, std::numeric_limits<T>::digits> {
86 struct negative_digits
87 :
public std::integral_constant<int, std::is_signed<T>::value ? std::numeric_limits<T>::digits : 0> {
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&)
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)
106 return source>
static_cast<Source
>(std::numeric_limits<Destination>::max());
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&)
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)
125 return source<static_cast<Source>(std::numeric_limits<Destination>::lowest());
131 template<
class OverflowTag,
class Operator,
class Enable =
void>
135 template<
class Result,
class Input>
136 constexpr Result convert(native_overflow_tag,
const Input& rhs)
138 return static_cast<Result
>(rhs);
141 template<
class Result,
class Input>
142 constexpr Result convert(throwing_overflow_tag,
const Input& rhs)
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");
155 template<
class Result,
class Input>
156 constexpr Result convert(saturated_overflow_tag,
const Input& rhs)
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);
172 namespace _overflow_impl {
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 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 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)),
196 "overflow in addition");
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>
206 using result_type = decltype(lhs+rhs);
207 using numeric_limits = std::numeric_limits<result_type>;
209 ? (lhs>numeric_limits::max()-rhs) ? numeric_limits::max() : lhs+rhs
210 : (lhs<numeric_limits::lowest()-rhs) ? numeric_limits::lowest() : lhs+rhs;
215 template<
class OverflowTag,
class Lhs,
class Rhs>
216 constexpr
auto add(OverflowTag,
const Lhs& lhs,
const Rhs& rhs)
219 return for_rep<decltype(lhs+rhs)>(_overflow_impl::operate<OverflowTag, _impl::add_op>(), lhs, rhs);
226 namespace _overflow_impl {
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 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 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),
250 "positive overflow in subtraction");
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>
260 using result_type = decltype(lhs-rhs);
261 using numeric_limits = std::numeric_limits<result_type>;
263 ? (lhs>numeric_limits::max()+rhs) ? numeric_limits::max() : lhs-rhs
264 : (lhs<numeric_limits::lowest()+rhs) ? numeric_limits::lowest() : lhs-rhs;
269 template<
class OverflowTag,
class Lhs,
class Rhs>
270 constexpr
auto subtract(OverflowTag,
const Lhs& lhs,
const Rhs& rhs)
273 return for_rep<decltype(lhs-rhs)>(_overflow_impl::operate<OverflowTag, _impl::subtract_op>(), lhs, rhs);
280 namespace _overflow_impl {
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 291 template<
class Lhs,
class Rhs>
292 constexpr
bool is_multiply_overflow(
const Lhs& lhs,
const Rhs& rhs)
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);
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 306 return _overflow_impl::return_if(
307 !is_multiply_overflow(lhs, rhs),
308 lhs*rhs,
"overflow in multiplication");
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>
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()
328 template<
class OverflowTag,
class Lhs,
class Rhs>
329 constexpr
auto multiply(OverflowTag,
const Lhs& lhs,
const Rhs& rhs)
332 return for_rep<decltype(lhs*rhs)>(_overflow_impl::operate<OverflowTag, _impl::multiply_op>(), lhs, rhs);
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 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))
363 return Operator()(lhs, rhs);
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>
374 return Operator()(lhs, rhs);
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>
387 using converted = decltype(lhs | rhs);
389 convert<converted>(saturated_overflow, lhs),
390 convert<converted>(saturated_overflow, rhs));
396 #endif //SG14_OVERFLOW_H study group 14 of the C++ working group
Definition: const_integer.h:22