File indexing completed on 2025-01-30 09:59:41
0001 #ifndef BOOST_NUMERIC_CHECKED_INTEGER_HPP
0002 #define BOOST_NUMERIC_CHECKED_INTEGER_HPP
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <limits>
0014 #include <type_traits> // is_integral, make_unsigned, enable_if
0015 #include <algorithm> // std::max
0016
0017 #include "checked_result.hpp"
0018 #include "checked_default.hpp"
0019 #include "safe_compare.hpp"
0020 #include "utility.hpp"
0021 #include "exception.hpp"
0022
0023 namespace boost {
0024 namespace safe_numerics {
0025
0026
0027
0028 template<bool tf>
0029 using bool_type = typename std::conditional<tf, std::true_type, std::false_type>::type;
0030
0031
0032
0033
0034
0035
0036 template<
0037 typename R,
0038 R Min,
0039 R Max,
0040 typename T,
0041 class F
0042 >
0043 struct heterogeneous_checked_operation<
0044 R,
0045 Min,
0046 Max,
0047 T,
0048 F,
0049 typename std::enable_if<
0050 std::is_integral<R>::value
0051 && std::is_integral<T>::value
0052 >::type
0053 >{
0054
0055
0056
0057 struct cast_impl_detail {
0058 constexpr static checked_result<R>
0059 cast_impl(
0060 const T & t,
0061 std::true_type,
0062 std::true_type
0063 ){
0064
0065
0066 return
0067 boost::safe_numerics::safe_compare::greater_than(t, Max) ?
0068 F::template invoke<safe_numerics_error::positive_overflow_error>(
0069 "converted signed value too large"
0070 )
0071 : boost::safe_numerics::safe_compare::less_than(t, Min) ?
0072 F::template invoke<safe_numerics_error::negative_overflow_error>(
0073 "converted signed value too small"
0074 )
0075 :
0076 checked_result<R>(static_cast<R>(t))
0077 ;
0078 }
0079 constexpr static checked_result<R>
0080 cast_impl(
0081 const T & t,
0082 std::true_type,
0083 std::false_type
0084 ){
0085
0086
0087 return
0088 boost::safe_numerics::safe_compare::greater_than(t, Max) ?
0089 F::template invoke<safe_numerics_error::positive_overflow_error>(
0090 "converted unsigned value too large"
0091 )
0092 :
0093 boost::safe_numerics::safe_compare::less_than(t, Min) ?
0094 F::template invoke<safe_numerics_error::positive_overflow_error>(
0095 "converted unsigned value too small"
0096 )
0097 :
0098 checked_result<R>(static_cast<R>(t))
0099 ;
0100 }
0101 constexpr static checked_result<R>
0102 cast_impl(
0103 const T & t,
0104 std::false_type,
0105 std::false_type
0106 ){
0107
0108
0109 return
0110 boost::safe_numerics::safe_compare::greater_than(t, Max) ?
0111 F::template invoke<safe_numerics_error::positive_overflow_error>(
0112 "converted unsigned value too large"
0113 )
0114 :
0115 boost::safe_numerics::safe_compare::less_than(t, Min) ?
0116 F::template invoke<safe_numerics_error::positive_overflow_error>(
0117 "converted unsigned value too small"
0118 )
0119 :
0120 checked_result<R>(static_cast<R>(t))
0121 ;
0122 }
0123 constexpr static checked_result<R>
0124 cast_impl(
0125 const T & t,
0126 std::false_type,
0127 std::true_type
0128 ){
0129 return
0130 boost::safe_numerics::safe_compare::less_than(t, Min) ?
0131 F::template invoke<safe_numerics_error::domain_error>(
0132 "converted value to low or negative"
0133 )
0134 :
0135 boost::safe_numerics::safe_compare::greater_than(t, Max) ?
0136 F::template invoke<safe_numerics_error::positive_overflow_error>(
0137 "converted signed value too large"
0138 )
0139 :
0140 checked_result<R>(static_cast<R>(t))
0141 ;
0142 }
0143 };
0144
0145 constexpr static checked_result<R>
0146 cast(const T & t){
0147 return
0148 cast_impl_detail::cast_impl(
0149 t,
0150 std::is_signed<R>(),
0151 std::is_signed<T>()
0152 );
0153 }
0154 };
0155
0156
0157 template<
0158 typename R,
0159 R Min,
0160 R Max,
0161 typename T,
0162 class F
0163 >
0164 struct heterogeneous_checked_operation<
0165 R,
0166 Min,
0167 Max,
0168 T,
0169 F,
0170 typename std::enable_if<
0171 std::is_integral<R>::value
0172 && std::is_floating_point<T>::value
0173 >::type
0174 >{
0175 constexpr static checked_result<R>
0176 cast(const T & t){
0177 return static_cast<R>(t);
0178 }
0179 };
0180
0181
0182
0183
0184 template<
0185 typename R,
0186 R Min,
0187 R Max,
0188 typename T,
0189 class F
0190 >
0191 struct heterogeneous_checked_operation<
0192 R,
0193 Min,
0194 Max,
0195 T,
0196 F,
0197 typename std::enable_if<
0198 std::is_floating_point<R>::value
0199 && std::is_integral<T>::value
0200 >::type
0201 >{
0202 constexpr static checked_result<R>
0203 cast(const T & t){
0204 if(std::numeric_limits<R>::digits < std::numeric_limits<T>::digits){
0205 if(utility::significant_bits(t) > std::numeric_limits<R>::digits){
0206 return F::invoke(
0207 safe_numerics_error::precision_overflow_error,
0208 "keep precision"
0209 );
0210 }
0211 }
0212 return t;
0213 }
0214 };
0215
0216
0217
0218 template<
0219 typename R,
0220 class F
0221 >
0222 struct checked_operation<R, F,
0223 typename std::enable_if<
0224 std::is_integral<R>::value
0225 >::type
0226 >{
0227
0228
0229
0230 struct add_impl_detail {
0231
0232 constexpr static checked_result<R> add(
0233 const R t,
0234 const R u,
0235 std::false_type
0236 ){
0237 return
0238
0239 std::numeric_limits<R>::max() - u < t ?
0240 F::template invoke<safe_numerics_error::positive_overflow_error>(
0241 "addition result too large"
0242 )
0243 :
0244 checked_result<R>(t + u)
0245 ;
0246 }
0247
0248
0249 constexpr static checked_result<R> add(
0250 const R t,
0251 const R u,
0252 std::true_type
0253 ){
0254
0255 return
0256
0257 ((u > 0) && (t > (std::numeric_limits<R>::max() - u))) ?
0258 F::template invoke<safe_numerics_error::positive_overflow_error>(
0259 "addition result too large"
0260 )
0261 :
0262 ((u < 0) && (t < (std::numeric_limits<R>::min() - u))) ?
0263 F::template invoke<safe_numerics_error::negative_overflow_error>(
0264 "addition result too low"
0265 )
0266 :
0267 checked_result<R>(t + u)
0268 ;
0269 }
0270 };
0271
0272 constexpr static checked_result<R>
0273 add(const R & t, const R & u){
0274 return add_impl_detail::add(t, u, std::is_signed<R>());
0275 }
0276
0277
0278
0279 struct subtract_impl_detail {
0280
0281
0282 constexpr static checked_result<R> subtract(
0283 const R t,
0284 const R u,
0285 std::false_type
0286 ){
0287
0288 return
0289 t < u ?
0290 F::template invoke<safe_numerics_error::negative_overflow_error>(
0291 "subtraction result cannot be negative"
0292 )
0293 :
0294 checked_result<R>(t - u)
0295 ;
0296 }
0297
0298
0299 constexpr static checked_result<R> subtract(
0300 const R t,
0301 const R u,
0302 std::true_type
0303 ){
0304 return
0305
0306 ((u > 0) && (t < (std::numeric_limits<R>::min() + u))) ?
0307 F::template invoke<safe_numerics_error::negative_overflow_error>(
0308 "subtraction result overflows result type"
0309 )
0310 :
0311 ((u < 0) && (t > (std::numeric_limits<R>::max() + u))) ?
0312 F::template invoke<safe_numerics_error::positive_overflow_error>(
0313 "subtraction result overflows result type"
0314 )
0315 :
0316 checked_result<R>(t - u)
0317 ;
0318 }
0319
0320 };
0321
0322 constexpr static checked_result<R> subtract(const R & t, const R & u){
0323 return subtract_impl_detail::subtract(t, u, std::is_signed<R>());
0324 }
0325
0326
0327
0328 struct minus_impl_detail {
0329
0330
0331 constexpr static checked_result<R> minus(
0332 const R t,
0333 std::false_type
0334 ){
0335 return t > 0 ?
0336 F::template invoke<safe_numerics_error::negative_overflow_error>(
0337 "minus unsigned would be negative"
0338 )
0339 :
0340
0341 checked_result<R>(0)
0342 ;
0343 }
0344
0345
0346 constexpr static checked_result<R> minus(
0347 const R t,
0348 std::true_type
0349 ){
0350 return t == std::numeric_limits<R>::min() ?
0351 F::template invoke<safe_numerics_error::positive_overflow_error>(
0352 "subtraction result overflows result type"
0353 )
0354 :
0355 checked_result<R>(-t)
0356 ;
0357 }
0358
0359 };
0360
0361 constexpr static checked_result<R> minus(const R & t){
0362 return minus_impl_detail::minus(t, std::is_signed<R>());
0363 }
0364
0365
0366
0367
0368 struct multiply_impl_detail {
0369
0370
0371 constexpr static checked_result<R> multiply(
0372 const R t,
0373 const R u,
0374 std::false_type,
0375 std::false_type
0376
0377 ){
0378
0379
0380
0381 using i_type = std::uintmax_t;
0382 return
0383 static_cast<i_type>(t) * static_cast<i_type>(u)
0384 > std::numeric_limits<R>::max() ?
0385 F::template invoke<safe_numerics_error::positive_overflow_error>(
0386 "multiplication overflow"
0387 )
0388 :
0389 checked_result<R>(t * u)
0390 ;
0391 }
0392 constexpr static checked_result<R> multiply(
0393 const R t,
0394 const R u,
0395 std::false_type,
0396 std::true_type
0397
0398 ){
0399
0400 return
0401 u > 0 && t > std::numeric_limits<R>::max() / u ?
0402 F::template invoke<safe_numerics_error::positive_overflow_error>(
0403 "multiplication overflow"
0404 )
0405 :
0406 checked_result<R>(t * u)
0407 ;
0408 }
0409
0410
0411 constexpr static checked_result<R> multiply(
0412 const R t,
0413 const R u,
0414 std::true_type,
0415 std::false_type
0416
0417 ){
0418
0419
0420
0421 using i_type = std::intmax_t;
0422 return
0423 (
0424 static_cast<i_type>(t) * static_cast<i_type>(u)
0425 > static_cast<i_type>(std::numeric_limits<R>::max())
0426 ) ?
0427 F::template invoke<safe_numerics_error::positive_overflow_error>(
0428 "multiplication overflow"
0429 )
0430 :
0431 (
0432 static_cast<i_type>(t) * static_cast<i_type>(u)
0433 < static_cast<i_type>(std::numeric_limits<R>::min())
0434 ) ?
0435 F::template invoke<safe_numerics_error::negative_overflow_error>(
0436 "multiplication overflow"
0437 )
0438 :
0439 checked_result<R>(t * u)
0440 ;
0441 }
0442 constexpr static checked_result<R> multiply(
0443 const R t,
0444 const R u,
0445 std::true_type,
0446 std::true_type
0447 ){
0448 return t > 0 ?
0449 u > 0 ?
0450 t > std::numeric_limits<R>::max() / u ?
0451 F::template invoke<safe_numerics_error::positive_overflow_error>(
0452 "multiplication overflow"
0453 )
0454 :
0455 checked_result<R>(t * u)
0456 :
0457 u < std::numeric_limits<R>::min() / t ?
0458 F::template invoke<safe_numerics_error::negative_overflow_error>(
0459 "multiplication overflow"
0460 )
0461 :
0462 checked_result<R>(t * u)
0463 :
0464 u > 0 ?
0465 t < std::numeric_limits<R>::min() / u ?
0466 F::template invoke<safe_numerics_error::negative_overflow_error>(
0467 "multiplication overflow"
0468 )
0469 :
0470 checked_result<R>(t * u)
0471 :
0472 t != 0 && u < std::numeric_limits<R>::max() / t ?
0473 F::template invoke<safe_numerics_error::positive_overflow_error>(
0474 "multiplication overflow"
0475 )
0476 :
0477 checked_result<R>(t * u)
0478 ;
0479 }
0480 };
0481
0482 constexpr static checked_result<R> multiply(const R & t, const R & u){
0483 return multiply_impl_detail::multiply(
0484 t,
0485 u,
0486 std::is_signed<R>(),
0487 std::integral_constant<
0488 bool,
0489 (sizeof(R) > sizeof(std::uintmax_t) / 2)
0490 >()
0491 );
0492 }
0493
0494
0495
0496
0497 struct divide_impl_detail {
0498 constexpr static checked_result<R> divide(
0499 const R & t,
0500 const R & u,
0501 std::false_type
0502 ){
0503 return t / u;
0504 }
0505
0506 constexpr static checked_result<R> divide(
0507 const R & t,
0508 const R & u,
0509 std::true_type
0510 ){
0511 return
0512 (u == -1 && t == std::numeric_limits<R>::min()) ?
0513 F::template invoke<safe_numerics_error::positive_overflow_error>(
0514 "result cannot be represented"
0515 )
0516 :
0517 checked_result<R>(t / u)
0518 ;
0519 }
0520 };
0521
0522
0523 constexpr static checked_result<R> divide(const R & t, const R & u){
0524 if(u == 0){
0525 return F::template invoke<safe_numerics_error::domain_error>(
0526 "divide by zero"
0527 );
0528 }
0529 return divide_impl_detail::divide(t, u, std::is_signed<R>());
0530 }
0531
0532
0533
0534
0535 struct modulus_impl_detail {
0536 constexpr static checked_result<R> modulus(
0537 const R & t,
0538 const R & u,
0539 std::false_type
0540 ){
0541 return t % u;
0542 }
0543
0544 constexpr static checked_result<R> modulus(
0545 const R & t,
0546 const R & u,
0547 std::true_type
0548 ){
0549 if(u >= 0)
0550 return t % u;
0551 checked_result<R> ux = checked::minus(u);
0552 if(ux.exception())
0553 return t;
0554 return t % static_cast<R>(ux);
0555 }
0556 };
0557
0558 constexpr static checked_result<R> modulus(const R & t, const R & u){
0559 if(0 == u)
0560 return F::template invoke<safe_numerics_error::domain_error>(
0561 "denominator is zero"
0562 );
0563
0564
0565
0566
0567
0568
0569
0570
0571 return modulus_impl_detail::modulus(t, u, typename std::is_signed<R>::type());
0572 }
0573
0574
0575
0576
0577 struct left_shift_integer_detail {
0578
0579 #if 0
0580
0581
0582
0583
0584
0585
0586
0587 #ifndef __has_feature
0588 #define __has_feature(x) 0
0589 #endif
0590
0591 template<typename T>
0592 constexpr unsigned int leading_zeros(const T & t){
0593 if(0 == t)
0594 return 0;
0595 #if __has_feature(builtin_clz)
0596 return __builtin_clz(t);
0597 #else
0598 #endif
0599 }
0600 #endif
0601
0602
0603
0604
0605
0606
0607 constexpr static checked_result<R> left_shift(
0608 const R & t,
0609 const R & u,
0610 std::false_type
0611 ){
0612
0613
0614
0615
0616
0617
0618 if(
0619 safe_compare::greater_than(
0620 u,
0621 std::numeric_limits<R>::digits - utility::significant_bits(t)
0622 )
0623 ){
0624
0625 return F::template invoke<safe_numerics_error::shift_too_large>(
0626 "shifting left more bits than available is undefined behavior"
0627 );
0628 }
0629 return t << u;
0630 }
0631
0632 constexpr static checked_result<R> left_shift(
0633 const R & t,
0634 const R & u,
0635 std::true_type
0636 ){
0637
0638 if(t >= 0){
0639
0640
0641
0642
0643
0644
0645 if(
0646 safe_compare::greater_than(
0647 u,
0648 std::numeric_limits<R>::digits - utility::significant_bits(t)
0649 )
0650 ){
0651
0652 return F::template invoke<safe_numerics_error::shift_too_large>(
0653 "shifting left more bits than available"
0654 );
0655 }
0656 else{
0657 return t << u;
0658 }
0659 }
0660
0661 return F::template invoke<safe_numerics_error::negative_shift>(
0662 "shifting a negative value"
0663 );
0664 }
0665
0666 };
0667
0668 constexpr static checked_result<R> left_shift(
0669 const R & t,
0670 const R & u
0671 ){
0672
0673
0674
0675
0676 if(u == 0){
0677 return t;
0678 }
0679 if(u < 0){
0680 return F::template invoke<safe_numerics_error::negative_shift>(
0681 "shifting negative amount"
0682 );
0683 }
0684 if(u > std::numeric_limits<R>::digits){
0685
0686 return F::template invoke<safe_numerics_error::shift_too_large>(
0687 "shifting more bits than available"
0688 );
0689 }
0690 return left_shift_integer_detail::left_shift(t, u, std::is_signed<R>());
0691 }
0692
0693
0694
0695 struct right_shift_integer_detail {
0696
0697
0698
0699
0700
0701
0702 constexpr static checked_result<R> right_shift(
0703 const R & t,
0704 const R & u,
0705 std::false_type
0706 ){
0707
0708
0709 return t >> u;
0710 }
0711
0712 constexpr static checked_result<R> right_shift(
0713 const R & t,
0714 const R & u,
0715 std::true_type
0716 ){
0717 if(t < 0){
0718
0719
0720 return F::template invoke<safe_numerics_error::negative_value_shift>(
0721 "shifting a negative value"
0722 );
0723 }
0724
0725
0726 return t >> u;
0727 }
0728 };
0729
0730 constexpr static checked_result<R> right_shift(
0731 const R & t,
0732 const R & u
0733 ){
0734
0735
0736
0737
0738 if(u < 0){
0739 return F::template invoke<safe_numerics_error::negative_shift>(
0740 "shifting negative amount"
0741 );
0742 }
0743 if(u > std::numeric_limits<R>::digits){
0744
0745 return F::template invoke<safe_numerics_error::shift_too_large>(
0746 "shifting more bits than available"
0747 );
0748 }
0749 return right_shift_integer_detail::right_shift(t, u ,std::is_signed<R>());
0750 }
0751
0752
0753
0754
0755
0756
0757
0758
0759 constexpr static checked_result<R> bitwise_or(const R & t, const R & u){
0760 using namespace boost::safe_numerics::utility;
0761 const unsigned int result_size
0762 = std::max(significant_bits(t), significant_bits(u));
0763
0764 if(result_size > bits_type<R>::value){
0765 return F::template invoke<safe_numerics_error::positive_overflow_error>(
0766 "result type too small to hold bitwise or"
0767 );
0768 }
0769 return t | u;
0770 }
0771
0772 constexpr static checked_result<R> bitwise_xor(const R & t, const R & u){
0773 using namespace boost::safe_numerics::utility;
0774 const unsigned int result_size
0775 = std::max(significant_bits(t), significant_bits(u));
0776
0777 if(result_size > bits_type<R>::value){
0778 return F::template invoke<safe_numerics_error::positive_overflow_error>(
0779 "result type too small to hold bitwise or"
0780 );
0781 }
0782 return t ^ u;
0783 }
0784
0785 constexpr static checked_result<R> bitwise_and(const R & t, const R & u){
0786 using namespace boost::safe_numerics::utility;
0787 const unsigned int result_size
0788 = std::min(significant_bits(t), significant_bits(u));
0789
0790 if(result_size > bits_type<R>::value){
0791 return F::template invoke<safe_numerics_error::positive_overflow_error>(
0792 "result type too small to hold bitwise and"
0793 );
0794 }
0795 return t & u;
0796 }
0797
0798 constexpr static checked_result<R> bitwise_not(const R & t){
0799 using namespace boost::safe_numerics::utility;
0800
0801 if(significant_bits(t) > bits_type<R>::value){
0802 return F::template invoke<safe_numerics_error::positive_overflow_error>(
0803 "result type too small to hold bitwise inverse"
0804 );
0805 }
0806 return ~t;
0807 }
0808
0809 };
0810 }
0811 }
0812
0813 #endif