1 #ifndef BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP
2 #define BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP
4 // Copyright (c) 2012 Robert Ramey
6 // Distributed under the Boost Software License, Version 1.0. (See
7 // accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
11 #include <type_traits> // is_base_of, is_same, is_floating_point, conditional
12 #include <algorithm> // max
15 #include <utility> // declval
17 #include <boost/config.hpp>
19 #include <boost/core/enable_if.hpp> // lazy_enable_if
20 #include <boost/integer.hpp>
21 #include <boost/logic/tribool.hpp>
23 #include "concept/numeric.hpp"
25 #include "checked_integer.hpp"
26 #include "checked_result.hpp"
27 #include "safe_base.hpp"
29 #include "interval.hpp"
30 #include "utility.hpp"
32 #include <boost/mp11/utility.hpp> // mp_valid
33 #include <boost/mp11/function.hpp> // mp_and, mp_or
36 namespace safe_numerics {
38 ////////////////////////////////////////////////////////////////////////////////
39 // compile time error dispatcher
41 // note slightly baroque implementation of a compile time switch statement
42 // which instatiates only those cases which are actually invoked. This is
43 // motivated to implement the "trap" functionality which will generate a syntax
44 // error if and only a function which might fail is called.
46 namespace dispatch_switch {
48 template<class EP, safe_numerics_actions>
49 struct dispatch_case {};
52 struct dispatch_case<EP, safe_numerics_actions::uninitialized_value> {
53 constexpr static void invoke(const safe_numerics_error & e, const char * msg){
54 EP::on_uninitialized_value(e, msg);
58 struct dispatch_case<EP, safe_numerics_actions::arithmetic_error> {
59 constexpr static void invoke(const safe_numerics_error & e, const char * msg){
60 EP::on_arithmetic_error(e, msg);
64 struct dispatch_case<EP, safe_numerics_actions::implementation_defined_behavior> {
65 constexpr static void invoke(const safe_numerics_error & e, const char * msg){
66 EP::on_implementation_defined_behavior(e, msg);
70 struct dispatch_case<EP, safe_numerics_actions::undefined_behavior> {
71 constexpr static void invoke(const safe_numerics_error & e, const char * msg){
72 EP::on_undefined_behavior(e, msg);
78 template<class EP, safe_numerics_error E>
80 dispatch(const char * msg){
81 constexpr safe_numerics_actions a = make_safe_numerics_action(E);
82 dispatch_switch::dispatch_case<EP, a>::invoke(E, msg);
85 template<class EP, class R>
86 class dispatch_and_return {
88 template<safe_numerics_error E>
89 constexpr static checked_result<R> invoke(
90 char const * const & msg
93 return checked_result<R>(E, msg);
97 /////////////////////////////////////////////////////////////////
100 template<typename R, R Min, R Max, typename E>
101 struct validate_detail {
102 using r_type = checked_result<R>;
104 struct exception_possible {
106 constexpr static R return_value(
110 const r_type rx = heterogeneous_checked_operation<
114 typename base_type<T>::type,
115 dispatch_and_return<E, R>
121 struct exception_not_possible {
123 constexpr static R return_value(
126 return static_cast<R>(base_value(t));
131 constexpr static R return_value(const T & t){
132 constexpr const interval<r_type> t_interval{
133 checked::cast<R>(base_value(std::numeric_limits<T>::min())),
134 checked::cast<R>(base_value(std::numeric_limits<T>::max()))
136 constexpr const interval<r_type> r_interval{r_type(Min), r_type(Max)};
139 true != static_cast<bool>(r_interval.excludes(t_interval)),
140 "can't cast from ranges that don't overlap"
142 return std::conditional<
143 static_cast<bool>(r_interval.includes(t_interval)),
144 exception_not_possible,
146 >::type::return_value(t);
150 template<class Stored, Stored Min, Stored Max, class P, class E>
152 constexpr inline Stored safe_base<Stored, Min, Max, P, E>::
153 validated_cast(const T & t) const {
154 return validate_detail<Stored,Min,Max,E>::return_value(t);
157 /////////////////////////////////////////////////////////////////
160 // default constructor
161 template<class Stored, Stored Min, Stored Max, class P, class E>
162 constexpr inline /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(){
164 std::is_arithmetic<Stored>(),
165 "currently, safe numeric base types must currently be arithmetic types"
167 dispatch<E, safe_numerics_error::uninitialized_value>(
168 "safe values must be initialized"
171 // construct an instance of a safe type from an instance of a convertible underlying type.
172 template<class Stored, Stored Min, Stored Max, class P, class E>
173 constexpr inline /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(
180 std::is_arithmetic<Stored>(),
181 "currently, safe numeric base types must currently be arithmetic types"
185 // construct an instance from an instance of a convertible underlying type.
186 template<class Stored, Stored Min, Stored Max, class P, class E>
189 typename std::enable_if<
190 std::is_convertible<T, Stored>::value,
194 constexpr inline /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(const T &t) :
195 m_t(validated_cast(t))
198 std::is_arithmetic<Stored>(),
199 "currently, safe numeric base types must currently be arithmetic types"
203 // construct an instance of a safe type from a literal value
204 template<class Stored, Stored Min, Stored Max, class P, class E>
205 template<typename T, T N, class Px, class Ex>
206 constexpr inline /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(
207 const safe_literal_impl<T, N, Px, Ex> & t
209 m_t(validated_cast(t))
211 std::is_arithmetic<Stored>(),
212 "currently, safe numeric base types must currently be arithmetic types"
216 /////////////////////////////////////////////////////////////////
219 // cast to a builtin type from a safe type
220 template< class Stored, Stored Min, Stored Max, class P, class E>
223 typename std::enable_if<
224 ! boost::safe_numerics::is_safe<R>::value,
228 constexpr inline safe_base<Stored, Min, Max, P, E>::
229 operator R () const {
230 // if static values don't overlap, the program can never function
231 constexpr const interval<R> r_interval;
232 constexpr const interval<Stored> this_interval(Min, Max);
234 ! r_interval.excludes(this_interval),
235 "safe type cannot be constructed with this type"
237 return validate_detail<
239 std::numeric_limits<R>::min(),
240 std::numeric_limits<R>::max(),
242 >::return_value(m_t);
245 /////////////////////////////////////////////////////////////////
248 template<class T, class U>
249 struct common_exception_policy {
250 static_assert(is_safe<T>::value || is_safe<U>::value,
251 "at least one type must be a safe type"
254 using t_exception_policy = typename get_exception_policy<T>::type;
255 using u_exception_policy = typename get_exception_policy<U>::type;
258 std::is_same<t_exception_policy, u_exception_policy>::value
259 || std::is_same<t_exception_policy, void>::value
260 || std::is_same<void, u_exception_policy>::value,
261 "if the exception policies are different, one must be void!"
265 ! (std::is_same<t_exception_policy, void>::value
266 && std::is_same<void, u_exception_policy>::value),
267 "at least one exception policy must not be void"
271 typename std::conditional<
272 !std::is_same<void, u_exception_policy>::value,
274 typename std::conditional<
275 !std::is_same<void, t_exception_policy>::value,
282 !std::is_same<void, type>::value,
283 "exception_policy is void"
287 template<class T, class U>
288 struct common_promotion_policy {
289 static_assert(is_safe<T>::value || is_safe<U>::value,
290 "at least one type must be a safe type"
292 using t_promotion_policy = typename get_promotion_policy<T>::type;
293 using u_promotion_policy = typename get_promotion_policy<U>::type;
295 std::is_same<t_promotion_policy, u_promotion_policy>::value
296 ||std::is_same<t_promotion_policy, void>::value
297 ||std::is_same<void, u_promotion_policy>::value,
298 "if the promotion policies are different, one must be void!"
301 ! (std::is_same<t_promotion_policy, void>::value
302 && std::is_same<void, u_promotion_policy>::value),
303 "at least one promotion policy must not be void"
307 typename std::conditional<
308 ! std::is_same<void, u_promotion_policy>::value,
310 typename std::conditional<
311 ! std::is_same<void, t_promotion_policy>::value,
318 ! std::is_same<void, type>::value,
319 "promotion_policy is void"
324 // give the resultant base type, figure out what the final result
325 // type will be. Note we currently need this because we support
326 // return of only safe integer types. Someday ..., we'll support
327 // all other safe types including float and user defined ones.
329 // helper - cast arguments to binary operators to a specified
332 template<class EP, class R, class T, class U>
333 constexpr inline static std::pair<R, R> casting_helper(const T & t, const U & u){
334 using r_type = checked_result<R>;
335 const r_type tx = heterogeneous_checked_operation<
337 std::numeric_limits<R>::min(),
338 std::numeric_limits<R>::max(),
339 typename base_type<T>::type,
340 dispatch_and_return<EP, R>
341 >::cast(base_value(t));
342 const R tr = tx.exception()
346 const r_type ux = heterogeneous_checked_operation<
348 std::numeric_limits<R>::min(),
349 std::numeric_limits<R>::max(),
350 typename base_type<U>::type,
351 dispatch_and_return<EP, R>
352 >::cast(base_value(u));
353 const R ur = ux.exception()
356 return std::pair<R, R>(tr, ur);
359 // Note: the following global operators will be found via
360 // argument dependent lookup.
362 template<template<class...> class F, class T, class U >
363 using legal_overload =
365 boost::mp11::mp_or< is_safe<T>, is_safe<U> >,
366 boost::mp11::mp_valid<
368 typename base_type<T>::type,
369 typename base_type<U>::type
374 /////////////////////////////////////////////////////////////////
377 template<class T, class U>
378 struct addition_result {
380 using promotion_policy = typename common_promotion_policy<T, U>::type;
381 using result_base_type =
382 typename promotion_policy::template addition_result<T,U>::type;
384 // if exception not possible
385 constexpr static result_base_type
386 return_value(const T & t, const U & u, std::false_type){
388 static_cast<result_base_type>(base_value(t))
389 + static_cast<result_base_type>(base_value(u));
392 // if exception possible
393 using exception_policy = typename common_exception_policy<T, U>::type;
395 using r_type = checked_result<result_base_type>;
397 constexpr static result_base_type
398 return_value(const T & t, const U & u, std::true_type){
399 const std::pair<result_base_type, result_base_type> r = casting_helper<
404 const r_type rx = checked_operation<
406 dispatch_and_return<exception_policy, result_base_type>
407 >::add(r.first, r.second);
415 using r_type_interval_t = interval<r_type>;
417 constexpr static const r_type_interval_t get_r_type_interval(){
418 constexpr const r_type_interval_t t_interval{
419 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
420 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
422 constexpr const r_type_interval_t u_interval{
423 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
424 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
426 return t_interval + u_interval;
428 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
430 constexpr static const interval<result_base_type> return_interval{
431 r_type_interval.l.exception()
432 ? std::numeric_limits<result_base_type>::min()
433 : static_cast<result_base_type>(r_type_interval.l),
434 r_type_interval.u.exception()
435 ? std::numeric_limits<result_base_type>::max()
436 : static_cast<result_base_type>(r_type_interval.u)
439 constexpr static bool exception_possible(){
440 if(r_type_interval.l.exception())
442 if(r_type_interval.u.exception())
444 if(! return_interval.includes(r_type_interval))
449 constexpr static auto rl = return_interval.l;
450 constexpr static auto ru = return_interval.u;
462 constexpr static type return_value(const T & t, const U & u){
467 std::integral_constant<bool, exception_possible()>()
469 typename type::skip_validation()
474 template<class T, class U> using addition_operator
475 = decltype( std::declval<T const&>() + std::declval<U const&>() );
477 template<class T, class U>
478 typename boost::lazy_enable_if_c<
479 legal_overload<addition_operator, T, U>::value,
480 addition_result<T, U>
482 constexpr inline operator+(const T & t, const U & u){
483 return addition_result<T, U>::return_value(t, u);
486 template<class T, class U>
487 typename std::enable_if<
488 legal_overload<addition_operator, T, U>::value,
491 constexpr inline operator+=(T & t, const U & u){
492 t = static_cast<T>(t + u);
496 /////////////////////////////////////////////////////////////////
499 template<class T, class U>
500 struct subtraction_result {
502 using promotion_policy = typename common_promotion_policy<T, U>::type;
503 using result_base_type =
504 typename promotion_policy::template subtraction_result<T, U>::type;
506 // if exception not possible
507 constexpr static result_base_type
508 return_value(const T & t, const U & u, std::false_type){
510 static_cast<result_base_type>(base_value(t))
511 - static_cast<result_base_type>(base_value(u));
514 // if exception possible
515 using exception_policy = typename common_exception_policy<T, U>::type;
517 using r_type = checked_result<result_base_type>;
519 constexpr static result_base_type
520 return_value(const T & t, const U & u, std::true_type){
521 const std::pair<result_base_type, result_base_type> r = casting_helper<
526 const r_type rx = checked_operation<
528 dispatch_and_return<exception_policy, result_base_type>
529 >::subtract(r.first, r.second);
536 using r_type_interval_t = interval<r_type>;
538 constexpr static const r_type_interval_t get_r_type_interval(){
539 constexpr const r_type_interval_t t_interval{
540 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
541 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
544 constexpr const r_type_interval_t u_interval{
545 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
546 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
549 return t_interval - u_interval;
551 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
553 constexpr static const interval<result_base_type> return_interval{
554 r_type_interval.l.exception()
555 ? std::numeric_limits<result_base_type>::min()
556 : static_cast<result_base_type>(r_type_interval.l),
557 r_type_interval.u.exception()
558 ? std::numeric_limits<result_base_type>::max()
559 : static_cast<result_base_type>(r_type_interval.u)
562 constexpr static bool exception_possible(){
563 if(r_type_interval.l.exception())
565 if(r_type_interval.u.exception())
567 if(! return_interval.includes(r_type_interval))
573 constexpr static auto rl = return_interval.l;
574 constexpr static auto ru = return_interval.u;
585 constexpr static type return_value(const T & t, const U & u){
590 std::integral_constant<bool, exception_possible()>()
592 typename type::skip_validation()
597 template<class T, class U> using subtraction_operator
598 = decltype( std::declval<T const&>() - std::declval<U const&>() );
600 template<class T, class U>
601 typename boost::lazy_enable_if_c<
602 legal_overload<subtraction_operator, T, U>::value,
603 subtraction_result<T, U>
605 constexpr inline operator-(const T & t, const U & u){
606 return subtraction_result<T, U>::return_value(t, u);
609 template<class T, class U>
610 typename std::enable_if<
611 legal_overload<subtraction_operator, T, U>::value,
614 constexpr inline operator-=(T & t, const U & u){
615 t = static_cast<T>(t - u);
619 /////////////////////////////////////////////////////////////////
622 template<class T, class U>
623 struct multiplication_result {
625 using promotion_policy = typename common_promotion_policy<T, U>::type;
626 using result_base_type =
627 typename promotion_policy::template multiplication_result<T, U>::type;
629 // if exception not possible
630 constexpr static result_base_type
631 return_value(const T & t, const U & u, std::false_type){
633 static_cast<result_base_type>(base_value(t))
634 * static_cast<result_base_type>(base_value(u));
637 // if exception possible
638 using exception_policy = typename common_exception_policy<T, U>::type;
640 using r_type = checked_result<result_base_type>;
642 constexpr static result_base_type
643 return_value(const T & t, const U & u, std::true_type){
644 const std::pair<result_base_type, result_base_type> r = casting_helper<
649 const r_type rx = checked_operation<
651 dispatch_and_return<exception_policy, result_base_type>
652 >::multiply(r.first, r.second);
660 using r_type_interval_t = interval<r_type>;
662 constexpr static r_type_interval_t get_r_type_interval(){
663 constexpr const r_type_interval_t t_interval{
664 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
665 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
668 constexpr const r_type_interval_t u_interval{
669 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
670 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
673 return t_interval * u_interval;
676 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
678 constexpr static const interval<result_base_type> return_interval{
679 r_type_interval.l.exception()
680 ? std::numeric_limits<result_base_type>::min()
681 : static_cast<result_base_type>(r_type_interval.l),
682 r_type_interval.u.exception()
683 ? std::numeric_limits<result_base_type>::max()
684 : static_cast<result_base_type>(r_type_interval.u)
687 constexpr static bool exception_possible(){
688 if(r_type_interval.l.exception())
690 if(r_type_interval.u.exception())
692 if(! return_interval.includes(r_type_interval))
697 constexpr static auto rl = return_interval.l;
698 constexpr static auto ru = return_interval.u;
710 constexpr static type return_value(const T & t, const U & u){
715 std::integral_constant<bool, exception_possible()>()
717 typename type::skip_validation()
722 template<class T, class U> using multiplication_operator
723 = decltype( std::declval<T const&>() * std::declval<U const&>() );
725 template<class T, class U>
726 typename boost::lazy_enable_if_c<
727 legal_overload<multiplication_operator, T, U>::value,
728 multiplication_result<T, U>
730 constexpr inline operator*(const T & t, const U & u){
731 return multiplication_result<T, U>::return_value(t, u);
734 template<class T, class U>
735 typename std::enable_if<
736 legal_overload<multiplication_operator, T, U>::value,
739 constexpr inline operator*=(T & t, const U & u){
740 t = static_cast<T>(t * u);
744 /////////////////////////////////////////////////////////////////
747 // key idea here - result will never be larger than T
748 template<class T, class U>
749 struct division_result {
751 using promotion_policy = typename common_promotion_policy<T, U>::type;
752 using result_base_type =
753 typename promotion_policy::template division_result<T, U>::type;
755 // if exception not possible
756 constexpr static result_base_type
757 return_value(const T & t, const U & u, std::false_type){
759 static_cast<result_base_type>(base_value(t))
760 / static_cast<result_base_type>(base_value(u));
763 // if exception possible
764 using exception_policy = typename common_exception_policy<T, U>::type;
766 constexpr static const int bits = std::min(
767 std::numeric_limits<std::uintmax_t>::digits,
768 std::max(std::initializer_list<int>{
769 std::numeric_limits<result_base_type>::digits,
770 std::numeric_limits<typename base_type<T>::type>::digits,
771 std::numeric_limits<typename base_type<U>::type>::digits
772 }) + (std::numeric_limits<result_base_type>::is_signed ? 1 : 0)
775 using r_type = checked_result<result_base_type>;
777 constexpr static result_base_type
778 return_value(const T & t, const U & u, std::true_type){
779 using temp_base = typename std::conditional<
780 std::numeric_limits<result_base_type>::is_signed,
781 typename boost::int_t<bits>::least,
782 typename boost::uint_t<bits>::least
784 using t_type = checked_result<temp_base>;
786 const std::pair<t_type, t_type> r = casting_helper<
791 const t_type rx = checked_operation<
793 dispatch_and_return<exception_policy, temp_base>
794 >::divide(r.first, r.second);
801 using r_type_interval_t = interval<r_type>;
803 constexpr static r_type_interval_t t_interval(){
804 return r_type_interval_t{
805 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
806 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
810 constexpr static r_type_interval_t u_interval(){
811 return r_type_interval_t{
812 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
813 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
817 constexpr static r_type_interval_t get_r_type_interval(){
818 constexpr const r_type_interval_t t = t_interval();
819 constexpr const r_type_interval_t u = u_interval();
821 if(u.u < r_type(0) || u.l > r_type(0))
823 return utility::minmax(
824 std::initializer_list<r_type> {
837 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
839 constexpr static const interval<result_base_type> return_interval{
840 r_type_interval.l.exception()
841 ? std::numeric_limits<result_base_type>::min()
842 : static_cast<result_base_type>(r_type_interval.l),
843 r_type_interval.u.exception()
844 ? std::numeric_limits<result_base_type>::max()
845 : static_cast<result_base_type>(r_type_interval.u)
848 constexpr static bool exception_possible(){
849 constexpr const r_type_interval_t ri = get_r_type_interval();
850 constexpr const r_type_interval_t ui = u_interval();
852 static_cast<bool>(ui.includes(r_type(0)))
857 constexpr static auto rl = return_interval.l;
858 constexpr static auto ru = return_interval.u;
870 constexpr static type return_value(const T & t, const U & u){
875 std::integral_constant<bool, exception_possible()>()
877 typename type::skip_validation()
882 template<class T, class U> using division_operator
883 = decltype( std::declval<T const&>() / std::declval<U const&>() );
885 template<class T, class U>
886 typename boost::lazy_enable_if_c<
887 legal_overload<division_operator, T, U>::value,
888 division_result<T, U>
890 constexpr inline operator/(const T & t, const U & u){
891 return division_result<T, U>::return_value(t, u);
894 template<class T, class U>
895 typename std::enable_if<
896 legal_overload<division_operator, T, U>::value,
899 constexpr inline operator/=(T & t, const U & u){
900 t = static_cast<T>(t / u);
904 /////////////////////////////////////////////////////////////////
907 template<class T, class U>
908 struct modulus_result {
910 using promotion_policy = typename common_promotion_policy<T, U>::type;
911 using result_base_type = typename promotion_policy::template modulus_result<T, U>::type;
913 // if exception not possible
914 constexpr static result_base_type
915 return_value(const T & t, const U & u, std::false_type){
917 static_cast<result_base_type>(base_value(t))
918 % static_cast<result_base_type>(base_value(u));
921 // if exception possible
922 using exception_policy = typename common_exception_policy<T, U>::type;
924 constexpr static const int bits = std::min(
925 std::numeric_limits<std::uintmax_t>::digits,
926 std::max(std::initializer_list<int>{
927 std::numeric_limits<result_base_type>::digits,
928 std::numeric_limits<typename base_type<T>::type>::digits,
929 std::numeric_limits<typename base_type<U>::type>::digits
930 }) + (std::numeric_limits<result_base_type>::is_signed ? 1 : 0)
933 using r_type = checked_result<result_base_type>;
935 constexpr static result_base_type
936 return_value(const T & t, const U & u, std::true_type){
937 using temp_base = typename std::conditional<
938 std::numeric_limits<result_base_type>::is_signed,
939 typename boost::int_t<bits>::least,
940 typename boost::uint_t<bits>::least
942 using t_type = checked_result<temp_base>;
944 const std::pair<t_type, t_type> r = casting_helper<
949 const t_type rx = checked_operation<
951 dispatch_and_return<exception_policy, temp_base>
952 >::modulus(r.first, r.second);
960 using r_type_interval_t = interval<r_type>;
962 constexpr static const r_type_interval_t t_interval(){
963 return r_type_interval_t{
964 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
965 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
969 constexpr static const r_type_interval_t u_interval(){
970 return r_type_interval_t{
971 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
972 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
976 constexpr static const r_type_interval_t get_r_type_interval(){
977 constexpr const r_type_interval_t t = t_interval();
978 constexpr const r_type_interval_t u = u_interval();
983 return utility::minmax(
984 std::initializer_list<r_type> {
997 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
999 constexpr static const interval<result_base_type> return_interval{
1000 r_type_interval.l.exception()
1001 ? std::numeric_limits<result_base_type>::min()
1002 : static_cast<result_base_type>(r_type_interval.l),
1003 r_type_interval.u.exception()
1004 ? std::numeric_limits<result_base_type>::max()
1005 : static_cast<result_base_type>(r_type_interval.u)
1008 constexpr static bool exception_possible(){
1009 constexpr const r_type_interval_t ri = get_r_type_interval();
1010 constexpr const r_type_interval_t ui = u_interval();
1012 static_cast<bool>(ui.includes(r_type(0)))
1014 || ri.u.exception();
1017 constexpr static auto rl = return_interval.l;
1018 constexpr static auto ru = return_interval.u;
1030 constexpr static type return_value(const T & t, const U & u){
1035 std::integral_constant<bool, exception_possible()>()
1037 typename type::skip_validation()
1042 template<class T, class U> using modulus_operator
1043 = decltype( std::declval<T const&>() % std::declval<U const&>() );
1045 template<class T, class U>
1046 typename boost::lazy_enable_if_c<
1047 legal_overload<modulus_operator, T, U>::value,
1048 modulus_result<T, U>
1050 constexpr inline operator%(const T & t, const U & u){
1051 // see https://en.wikipedia.org/wiki/Modulo_operation
1052 return modulus_result<T, U>::return_value(t, u);
1055 template<class T, class U>
1056 typename std::enable_if<
1057 legal_overload<modulus_operator, T, U>::value,
1060 constexpr inline operator%=(T & t, const U & u){
1061 t = static_cast<T>(t % u);
1065 /////////////////////////////////////////////////////////////////
1070 template<class T, class U>
1071 struct less_than_result {
1073 using promotion_policy = typename common_promotion_policy<T, U>::type;
1075 using result_base_type =
1076 typename promotion_policy::template comparison_result<T, U>::type;
1078 // if exception not possible
1079 constexpr static bool
1080 return_value(const T & t, const U & u, std::false_type){
1082 static_cast<result_base_type>(base_value(t))
1083 < static_cast<result_base_type>(base_value(u));
1086 using exception_policy = typename common_exception_policy<T, U>::type;
1088 using r_type = checked_result<result_base_type>;
1090 // if exception possible
1091 constexpr static bool
1092 return_value(const T & t, const U & u, std::true_type){
1093 const std::pair<result_base_type, result_base_type> r = casting_helper<
1098 return safe_compare::less_than(r.first, r.second);
1101 using r_type_interval_t = interval<r_type>;
1103 constexpr static bool interval_open(const r_type_interval_t & t){
1104 return t.l.exception() || t.u.exception();
1108 constexpr static bool
1109 return_value(const T & t, const U & u){
1110 constexpr const r_type_interval_t t_interval{
1111 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
1112 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
1114 constexpr const r_type_interval_t u_interval{
1115 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
1116 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
1119 if(t_interval < u_interval)
1121 if(t_interval > u_interval)
1124 constexpr bool exception_possible
1125 = interval_open(t_interval) || interval_open(u_interval);
1127 return return_value(
1130 std::integral_constant<bool, exception_possible>()
1135 template<class T, class U> using less_than_operator
1136 = decltype( std::declval<T const&>() < std::declval<U const&>() );
1137 template<class T, class U> using greater_than_operator
1138 = decltype( std::declval<T const&>() > std::declval<U const&>() );
1139 template<class T, class U> using less_than_or_equal_operator
1140 = decltype( std::declval<T const&>() <= std::declval<U const&>() );
1141 template<class T, class U> using greater_than_or_equal_operator
1142 = decltype( std::declval<T const&>() >= std::declval<U const&>() );
1144 template<class T, class U>
1145 typename std::enable_if<
1146 legal_overload<less_than_operator, T, U>::value,
1149 constexpr inline operator<(const T & lhs, const U & rhs) {
1150 return less_than_result<T, U>::return_value(lhs, rhs);
1153 template<class T, class U>
1154 typename std::enable_if<
1155 legal_overload<greater_than_operator, T, U>::value,
1158 constexpr inline operator>(const T & lhs, const U & rhs) {
1162 template<class T, class U>
1163 typename std::enable_if<
1164 legal_overload<greater_than_or_equal_operator, T, U>::value,
1167 constexpr inline operator>=(const T & lhs, const U & rhs) {
1168 return ! ( lhs < rhs );
1171 template<class T, class U>
1172 typename std::enable_if<
1173 legal_overload<less_than_or_equal_operator, T, U>::value,
1176 constexpr inline operator<=(const T & lhs, const U & rhs) {
1177 return ! ( lhs > rhs );
1182 template<class T, class U>
1183 struct equal_result {
1185 using promotion_policy = typename common_promotion_policy<T, U>::type;
1187 using result_base_type =
1188 typename promotion_policy::template comparison_result<T, U>::type;
1190 // if exception not possible
1191 constexpr static bool
1192 return_value(const T & t, const U & u, std::false_type){
1194 static_cast<result_base_type>(base_value(t))
1195 == static_cast<result_base_type>(base_value(u));
1198 using exception_policy = typename common_exception_policy<T, U>::type;
1200 using r_type = checked_result<result_base_type>;
1202 // exception possible
1203 constexpr static bool
1204 return_value(const T & t, const U & u, std::true_type){
1205 const std::pair<result_base_type, result_base_type> r = casting_helper<
1210 return safe_compare::equal(r.first, r.second);
1213 using r_type_interval = interval<r_type>;
1215 constexpr static bool interval_open(const r_type_interval & t){
1216 return t.l.exception() || t.u.exception();
1220 constexpr static bool
1221 return_value(const T & t, const U & u){
1222 constexpr const r_type_interval t_interval{
1223 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
1224 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
1227 constexpr const r_type_interval u_interval{
1228 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
1229 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
1232 if(! intersect(t_interval, u_interval))
1235 constexpr bool exception_possible
1236 = interval_open(t_interval) || interval_open(u_interval);
1238 return return_value(
1241 std::integral_constant<bool, exception_possible>()
1246 template<class T, class U> using equal_to_operator
1247 = decltype( std::declval<T const&>() == std::declval<U const&>() );
1248 template<class T, class U> using not_equal_to_operator
1249 = decltype( std::declval<T const&>() != std::declval<U const&>() );
1251 template<class T, class U>
1252 typename std::enable_if<
1253 legal_overload<equal_to_operator, T, U>::value,
1256 constexpr inline operator==(const T & lhs, const U & rhs) {
1257 return equal_result<T, U>::return_value(lhs, rhs);
1260 template<class T, class U>
1261 typename std::enable_if<
1262 legal_overload<not_equal_to_operator, T, U>::value,
1265 constexpr inline operator!=(const T & lhs, const U & rhs) {
1266 return ! (lhs == rhs);
1269 /////////////////////////////////////////////////////////////////////////
1270 // The following operators only make sense when applied to integet types
1272 /////////////////////////////////////////////////////////////////////////
1276 template<class T, class U>
1277 struct left_shift_result {
1279 using promotion_policy = typename common_promotion_policy<T, U>::type;
1280 using result_base_type =
1281 typename promotion_policy::template left_shift_result<T, U>::type;
1283 // if exception not possible
1284 constexpr static result_base_type
1285 return_value(const T & t, const U & u, std::false_type){
1287 static_cast<result_base_type>(base_value(t))
1288 << static_cast<result_base_type>(base_value(u));
1291 // exception possible
1292 using exception_policy = typename common_exception_policy<T, U>::type;
1294 using r_type = checked_result<result_base_type>;
1296 constexpr static result_base_type
1297 return_value(const T & t, const U & u, std::true_type){
1298 const std::pair<result_base_type, result_base_type> r = casting_helper<
1303 const r_type rx = checked_operation<
1305 dispatch_and_return<exception_policy, result_base_type>
1306 >::left_shift(r.first, r.second);
1310 ? r.first << r.second
1311 : rx.m_contents.m_r;
1314 using r_type_interval_t = interval<r_type>;
1316 constexpr static r_type_interval_t get_r_type_interval(){
1317 constexpr const r_type_interval_t t_interval{
1318 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
1319 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
1322 constexpr const r_type_interval_t u_interval{
1323 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
1324 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
1326 return (t_interval << u_interval);
1329 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
1331 constexpr static const interval<result_base_type> return_interval{
1332 r_type_interval.l.exception()
1333 ? std::numeric_limits<result_base_type>::min()
1334 : static_cast<result_base_type>(r_type_interval.l),
1335 r_type_interval.u.exception()
1336 ? std::numeric_limits<result_base_type>::max()
1337 : static_cast<result_base_type>(r_type_interval.u)
1340 constexpr static bool exception_possible(){
1341 if(r_type_interval.l.exception())
1343 if(r_type_interval.u.exception())
1345 if(! return_interval.includes(r_type_interval))
1350 constexpr static const auto rl = return_interval.l;
1351 constexpr static const auto ru = return_interval.u;
1363 constexpr static type return_value(const T & t, const U & u){
1368 std::integral_constant<bool, exception_possible()>()
1370 typename type::skip_validation()
1375 template<class T, class U> using left_shift_operator
1376 = decltype( std::declval<T const&>() << std::declval<U const&>() );
1378 template<class T, class U>
1379 typename boost::lazy_enable_if_c<
1380 // exclude usage of << for file input here
1381 boost::safe_numerics::Numeric<T>()
1382 && legal_overload<left_shift_operator, T, U>::value,
1383 left_shift_result<T, U>
1385 constexpr inline operator<<(const T & t, const U & u){
1387 // C++ standards document N4618 & 5.8.2
1388 return left_shift_result<T, U>::return_value(t, u);
1391 template<class T, class U>
1392 typename std::enable_if<
1393 // exclude usage of << for file output here
1394 boost::safe_numerics::Numeric<T>()
1395 && legal_overload<left_shift_operator, T, U>::value,
1398 constexpr inline operator<<=(T & t, const U & u){
1399 t = static_cast<T>(t << u);
1403 template<class T, class CharT, class Traits> using stream_output_operator
1404 = decltype( std::declval<std::basic_ostream<CharT, Traits> &>() >> std::declval<T const&>() );
1406 template<class T, class CharT, class Traits>
1407 typename boost::lazy_enable_if_c<
1408 boost::mp11::mp_valid< stream_output_operator, T, CharT, Traits>::value,
1409 std::basic_ostream<CharT, Traits> &
1411 constexpr inline operator>>(
1412 std::basic_ostream<CharT, Traits> & os,
1416 // C++ standards document N4618 & 5.8.2
1421 /////////////////////////////////////////////////////////////////
1423 template<class T, class U>
1424 struct right_shift_result {
1425 using promotion_policy = typename common_promotion_policy<T, U>::type;
1426 using result_base_type =
1427 typename promotion_policy::template right_shift_result<T, U>::type;
1429 // if exception not possible
1430 constexpr static result_base_type
1431 return_value(const T & t, const U & u, std::false_type){
1433 static_cast<result_base_type>(base_value(t))
1434 >> static_cast<result_base_type>(base_value(u));
1437 // exception possible
1438 using exception_policy = typename common_exception_policy<T, U>::type;
1440 using r_type = checked_result<result_base_type>;
1442 constexpr static result_base_type
1443 return_value(const T & t, const U & u, std::true_type){
1444 const std::pair<result_base_type, result_base_type> r = casting_helper<
1449 const r_type rx = checked_operation<
1451 dispatch_and_return<exception_policy, result_base_type>
1452 >::right_shift(r.first, r.second);
1456 ? r.first >> r.second
1457 : rx.m_contents.m_r;
1460 using r_type_interval_t = interval<r_type>;
1462 constexpr static r_type_interval_t t_interval(){
1463 return r_type_interval_t(
1464 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
1465 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
1469 constexpr static r_type_interval_t u_interval(){
1470 return r_type_interval_t(
1471 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
1472 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
1475 constexpr static r_type_interval_t get_r_type_interval(){;
1476 return (t_interval() >> u_interval());
1479 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
1481 constexpr static const interval<result_base_type> return_interval{
1482 r_type_interval.l.exception()
1483 ? std::numeric_limits<result_base_type>::min()
1484 : static_cast<result_base_type>(r_type_interval.l),
1485 r_type_interval.u.exception()
1486 ? std::numeric_limits<result_base_type>::max()
1487 : static_cast<result_base_type>(r_type_interval.u)
1490 constexpr static bool exception_possible(){
1491 constexpr const r_type_interval_t ri = r_type_interval;
1492 constexpr const r_type_interval_t ti = t_interval();
1493 constexpr const r_type_interval_t ui = u_interval();
1494 return static_cast<bool>(
1495 // note undesirable coupling with checked::shift right here !
1496 ui.u > checked_result<result_base_type>(
1497 std::numeric_limits<result_base_type>::digits
1499 || ti.l < checked_result<result_base_type>(0)
1500 || ui.l < checked_result<result_base_type>(0)
1506 constexpr static auto rl = return_interval.l;
1507 constexpr static auto ru = return_interval.u;
1519 constexpr static type return_value(const T & t, const U & u){
1524 std::integral_constant<bool, exception_possible()>()
1526 typename type::skip_validation()
1531 template<class T, class U> using right_shift_operator
1532 = decltype( std::declval<T const&>() >> std::declval<U const&>() );
1534 template<class T, class U>
1535 typename boost::lazy_enable_if_c<
1536 // exclude usage of >> for file input here
1537 boost::safe_numerics::Numeric<T>()
1538 && legal_overload<right_shift_operator, T, U>::value,
1539 right_shift_result<T, U>
1541 constexpr inline operator>>(const T & t, const U & u){
1543 // C++ standards document N4618 & 5.8.2
1544 return right_shift_result<T, U>::return_value(t, u);
1547 template<class T, class U>
1548 typename std::enable_if<
1549 // exclude usage of << for file output here
1550 boost::safe_numerics::Numeric<T>()
1551 && legal_overload<right_shift_operator, T, U>::value,
1554 constexpr inline operator>>=(T & t, const U & u){
1555 t = static_cast<T>(t >> u);
1559 template<class T, class CharT, class Traits> using stream_input_operator
1560 = decltype( std::declval<std::basic_istream<CharT, Traits> &>() >> std::declval<T const&>() );
1562 template<class T, class CharT, class Traits>
1563 typename boost::lazy_enable_if_c<
1564 boost::mp11::mp_valid< stream_input_operator, T, CharT, Traits>::value,
1565 std::basic_istream<CharT, Traits> &
1567 constexpr inline operator>>(
1568 std::basic_istream<CharT, Traits> & is,
1572 // C++ standards document N4618 & 5.8.2
1577 /////////////////////////////////////////////////////////////////
1578 // bitwise operators
1581 template<class T, class U>
1582 struct bitwise_or_result {
1584 using promotion_policy = typename common_promotion_policy<T, U>::type;
1585 using result_base_type =
1586 typename promotion_policy::template bitwise_or_result<T, U>::type;
1588 // according to the C++ standard, the bitwise operators are executed as if
1589 // the operands are consider a logical array of bits. That is, there is no
1590 // sense that these are signed numbers.
1592 using r_type = typename std::make_unsigned<result_base_type>::type;
1593 using r_type_interval_t = interval<r_type>;
1594 using exception_policy = typename common_exception_policy<T, U>::type;
1597 // lazy_enable_if_c depends on this
1598 using type = safe_base<
1605 static_cast<r_type>(base_value(std::numeric_limits<T>::max())),
1606 static_cast<r_type>(base_value(std::numeric_limits<U>::max()))
1613 constexpr static type return_value(const T & t, const U & u){
1615 static_cast<result_base_type>(base_value(t))
1616 | static_cast<result_base_type>(base_value(u)),
1617 typename type::skip_validation()
1622 template<class T, class U> using bitwise_or_operator
1623 = decltype( std::declval<T const&>() | std::declval<U const&>() );
1625 template<class T, class U>
1626 typename boost::lazy_enable_if_c<
1627 legal_overload<bitwise_or_operator, T, U>::value,
1628 bitwise_or_result<T, U>
1630 constexpr inline operator|(const T & t, const U & u){
1631 return bitwise_or_result<T, U>::return_value(t, u);
1634 template<class T, class U>
1635 typename std::enable_if<
1636 legal_overload<bitwise_or_operator, T, U>::value,
1639 constexpr inline operator|=(T & t, const U & u){
1640 t = static_cast<T>(t | u);
1645 template<class T, class U>
1646 struct bitwise_and_result {
1648 using promotion_policy = typename common_promotion_policy<T, U>::type;
1649 using result_base_type =
1650 typename promotion_policy::template bitwise_and_result<T, U>::type;
1652 // according to the C++ standard, the bitwise operators are executed as if
1653 // the operands are consider a logical array of bits. That is, there is no
1654 // sense that these are signed numbers.
1656 using r_type = typename std::make_unsigned<result_base_type>::type;
1657 using r_type_interval_t = interval<r_type>;
1658 using exception_policy = typename common_exception_policy<T, U>::type;
1661 // lazy_enable_if_c depends on this
1662 using type = safe_base<
1669 static_cast<r_type>(base_value(std::numeric_limits<T>::max())),
1670 static_cast<r_type>(base_value(std::numeric_limits<U>::max()))
1677 constexpr static type return_value(const T & t, const U & u){
1679 static_cast<result_base_type>(base_value(t))
1680 & static_cast<result_base_type>(base_value(u)),
1681 typename type::skip_validation()
1686 template<class T, class U> using bitwise_and_operator
1687 = decltype( std::declval<T const&>() & std::declval<U const&>() );
1689 template<class T, class U>
1690 typename boost::lazy_enable_if_c<
1691 legal_overload<bitwise_and_operator, T, U>::value,
1692 bitwise_and_result<T, U>
1694 constexpr inline operator&(const T & t, const U & u){
1695 return bitwise_and_result<T, U>::return_value(t, u);
1698 template<class T, class U>
1699 typename std::enable_if<
1700 legal_overload<bitwise_and_operator, T, U>::value,
1703 constexpr inline operator&=(T & t, const U & u){
1704 t = static_cast<T>(t & u);
1709 template<class T, class U>
1710 struct bitwise_xor_result {
1711 using promotion_policy = typename common_promotion_policy<T, U>::type;
1712 using result_base_type =
1713 typename promotion_policy::template bitwise_xor_result<T, U>::type;
1715 // according to the C++ standard, the bitwise operators are executed as if
1716 // the operands are consider a logical array of bits. That is, there is no
1717 // sense that these are signed numbers.
1719 using r_type = typename std::make_unsigned<result_base_type>::type;
1720 using r_type_interval_t = interval<r_type>;
1721 using exception_policy = typename common_exception_policy<T, U>::type;
1724 // lazy_enable_if_c depends on this
1725 using type = safe_base<
1732 static_cast<r_type>(base_value(std::numeric_limits<T>::max())),
1733 static_cast<r_type>(base_value(std::numeric_limits<U>::max()))
1740 constexpr static type return_value(const T & t, const U & u){
1742 static_cast<result_base_type>(base_value(t))
1743 ^ static_cast<result_base_type>(base_value(u)),
1744 typename type::skip_validation()
1749 template<class T, class U> using bitwise_xor_operator
1750 = decltype( std::declval<T const&>() ^ std::declval<U const&>() );
1752 template<class T, class U>
1753 typename boost::lazy_enable_if_c<
1754 legal_overload<bitwise_xor_operator, T, U>::value,
1755 bitwise_xor_result<T, U>
1757 constexpr inline operator^(const T & t, const U & u){
1758 return bitwise_xor_result<T, U>::return_value(t, u);
1761 template<class T, class U>
1762 typename std::enable_if<
1763 legal_overload<bitwise_xor_operator, T, U>::value,
1766 constexpr inline operator^=(T & t, const U & u){
1767 t = static_cast<T>(t ^ u);
1771 /////////////////////////////////////////////////////////////////
1778 class P, // promotion polic
1779 class E // exception policy
1785 inline void safe_base<T, Min, Max, P, E>::output(
1786 std::basic_ostream<CharT, Traits> & os
1789 (std::is_same<T, signed char>::value
1790 || std::is_same<T, unsigned char>::value
1791 || std::is_same<T, wchar_t>::value
1793 static_cast<int>(m_t)
1803 class P, // promotion polic
1804 class E // exception policy
1810 inline void safe_base<T, Min, Max, P, E>::input(
1811 std::basic_istream<CharT, Traits> & is
1813 if(std::is_same<T, signed char>::value
1814 || std::is_same<T, unsigned char>::value
1815 || std::is_same<T, wchar_t>::value
1819 m_t = validated_cast(x);
1822 if(std::is_unsigned<T>::value){
1823 // reading a negative number into an unsigned variable cannot result in
1824 // a correct result. But, C++ reads the absolute value, multiplies
1825 // it by -1 and stores the resulting value. This is crazy - but there
1826 // it is! Oh, and it doesn't set the failbit. We fix this behavior here
1829 // if the input string starts with a '-', we know its an error
1832 is.setstate(std::ios_base::failbit);
1837 boost::safe_numerics::dispatch<
1839 boost::safe_numerics::safe_numerics_error::domain_error
1841 "error in file input"
1845 validated_cast(m_t);
1852 #endif // BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP