1 #ifndef BOOST_NUMERIC_AUTOMATIC_HPP
2 #define BOOST_NUMERIC_AUTOMATIC_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)
10 // policy which creates expanded results types designed
11 // to avoid overflows.
14 #include <cstdint> // (u)intmax_t,
15 #include <type_traits> // conditional
16 #include <boost/integer.hpp>
18 #include "safe_common.hpp"
19 #include "checked_result.hpp"
20 #include "checked_default.hpp"
21 #include "checked_integer.hpp"
22 #include "checked_result_operations.hpp"
23 #include "interval.hpp"
24 #include "utility.hpp"
27 namespace safe_numerics {
31 // the following returns the "true" type. After calculating the new max and min
32 // these return the minimum size type which can hold the expected result.
33 struct defer_stored_signed_lazily {
34 template<std::intmax_t Min, std::intmax_t Max>
35 using type = utility::signed_stored_type<Min, Max>;
38 struct defer_stored_unsigned_lazily {
39 template<std::uintmax_t Min, std::uintmax_t Max>
40 using type = utility::unsigned_stored_type<Min, Max>;
43 template<typename T, T Min, T Max>
45 using type = typename std::conditional<
46 std::numeric_limits<T>::is_signed,
47 defer_stored_signed_lazily,
48 defer_stored_unsigned_lazily
49 >::type::template type<Min, Max>;
53 ///////////////////////////////////////////////////////////////////////
54 template<typename T, typename U>
55 struct addition_result {
56 using temp_base_type = typename std::conditional<
57 // if both arguments are unsigned
58 ! std::numeric_limits<T>::is_signed
59 && ! std::numeric_limits<U>::is_signed,
62 // otherwise result is signed
66 using r_type = checked_result<temp_base_type>;
67 using r_interval_type = interval<r_type>;
69 constexpr static const r_interval_type t_interval{
70 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
71 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
74 constexpr static const r_interval_type u_interval{
75 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
76 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
79 constexpr static const r_interval_type r_interval = t_interval + u_interval;
81 constexpr static auto rl = r_interval.l;
82 constexpr static auto ru = r_interval.u;
84 using type = typename result_type<
87 ? std::numeric_limits<temp_base_type>::min()
88 : static_cast<temp_base_type>(rl),
90 ? std::numeric_limits<temp_base_type>::max()
91 : static_cast<temp_base_type>(ru)
95 ///////////////////////////////////////////////////////////////////////
96 template<typename T, typename U>
97 struct subtraction_result {
98 // result of subtraction are always signed.
99 using temp_base_type = intmax_t;
101 using r_type = checked_result<temp_base_type>;
102 using r_interval_type = interval<r_type>;
104 constexpr static const r_interval_type t_interval{
105 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
106 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
109 constexpr static const r_interval_type u_interval{
110 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
111 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
114 constexpr static const r_interval_type r_interval = t_interval - u_interval;
116 constexpr static auto rl = r_interval.l;
117 constexpr static auto ru = r_interval.u;
119 using type = typename result_type<
122 ? std::numeric_limits<temp_base_type>::min()
123 : static_cast<temp_base_type>(rl),
125 ? std::numeric_limits<temp_base_type>::max()
126 : static_cast<temp_base_type>(ru)
130 ///////////////////////////////////////////////////////////////////////
131 template<typename T, typename U>
132 struct multiplication_result {
133 using temp_base_type = typename std::conditional<
134 // if both arguments are unsigned
135 ! std::numeric_limits<T>::is_signed
136 && ! std::numeric_limits<U>::is_signed,
137 // result is unsigned
139 // otherwise result is signed
143 using r_type = checked_result<temp_base_type>;
144 using r_interval_type = interval<r_type>;
146 constexpr static const r_interval_type t_interval{
147 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
148 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
151 constexpr static const r_interval_type u_interval{
152 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
153 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
156 constexpr static const r_interval_type r_interval = t_interval * u_interval;
158 constexpr static const auto rl = r_interval.l;
159 constexpr static const auto ru = r_interval.u;
161 using type = typename result_type<
164 ? std::numeric_limits<temp_base_type>::min()
165 : static_cast<temp_base_type>(rl),
167 ? std::numeric_limits<temp_base_type>::max()
168 : static_cast<temp_base_type>(ru)
172 ///////////////////////////////////////////////////////////////////////
173 template<typename T, typename U>
174 struct division_result {
175 using temp_base_type = typename std::conditional<
176 // if both arguments are unsigned
177 ! std::numeric_limits<T>::is_signed
178 && ! std::numeric_limits<U>::is_signed,
179 // result is unsigned
181 // otherwise result is signed
185 using r_type = checked_result<temp_base_type>;
186 using r_interval_type = interval<r_type>;
188 constexpr static const r_interval_type t_interval{
189 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
190 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
193 constexpr static const r_interval_type u_interval{
194 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
195 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
198 constexpr static r_interval_type rx(){
199 if(u_interval.u < r_type(0)
200 || u_interval.l > r_type(0))
201 return t_interval / u_interval;
202 return utility::minmax(
203 std::initializer_list<r_type> {
204 t_interval.l / u_interval.l,
205 t_interval.l / r_type(-1),
206 t_interval.l / r_type(1),
207 t_interval.l / u_interval.u,
208 t_interval.u / u_interval.l,
209 t_interval.u / r_type(-1),
210 t_interval.u / r_type(1),
211 t_interval.u / u_interval.u,
216 constexpr static const r_interval_type r_interval = rx();
218 constexpr static auto rl = r_interval.l;
219 constexpr static auto ru = r_interval.u;
221 using type = typename result_type<
224 ? std::numeric_limits<temp_base_type>::min()
225 : static_cast<temp_base_type>(rl),
227 ? std::numeric_limits<temp_base_type>::max()
228 : static_cast<temp_base_type>(ru)
232 ///////////////////////////////////////////////////////////////////////
233 template<typename T, typename U>
234 struct modulus_result {
235 using temp_base_type = typename std::conditional<
236 // if both arguments are unsigned
237 ! std::numeric_limits<T>::is_signed
238 && ! std::numeric_limits<U>::is_signed,
239 // result is unsigned
241 // otherwise result is signed
245 using r_type = checked_result<temp_base_type>;
246 using r_interval_type = interval<r_type>;
248 constexpr static const r_interval_type t_interval{
249 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
250 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
253 constexpr static const r_interval_type u_interval{
254 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
255 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
258 constexpr static r_interval_type rx(){
259 if(u_interval.u < r_type(0)
260 || u_interval.l > r_type(0))
261 return t_interval / u_interval;
262 return utility::minmax(
263 std::initializer_list<r_type> {
264 t_interval.l % u_interval.l,
265 t_interval.l % r_type(-1),
266 t_interval.l % r_type(1),
267 t_interval.l % u_interval.u,
268 t_interval.u % u_interval.l,
269 t_interval.u % r_type(-1),
270 t_interval.u % r_type(1),
271 t_interval.u % u_interval.u,
276 constexpr static const r_interval_type r_interval = rx();
278 constexpr static auto rl = r_interval.l;
279 constexpr static auto ru = r_interval.u;
281 using type = typename result_type<
284 ? std::numeric_limits<temp_base_type>::min()
285 : static_cast<temp_base_type>(rl),
287 ? std::numeric_limits<temp_base_type>::max()
288 : static_cast<temp_base_type>(ru)
292 ///////////////////////////////////////////////////////////////////////
293 // note: comparison_result (<, >, ...) is special.
294 // The return value is always a bool. The type returned here is
295 // the intermediate type applied to make the values comparable.
296 template<typename T, typename U>
297 struct comparison_result {
298 using temp_base_type = typename std::conditional<
299 // if both arguments are unsigned
300 ! std::numeric_limits<T>::is_signed
301 && ! std::numeric_limits<U>::is_signed,
302 // result is unsigned
304 // otherwise result is signed
308 using r_type = checked_result<temp_base_type>;
309 using r_interval_type = interval<r_type>;
311 constexpr static const r_interval_type t_interval{
312 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
313 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
316 constexpr static const r_interval_type u_interval{
317 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
318 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
321 // workaround some microsoft problem
323 constexpr static r_type min(const r_type & t, const r_type & u){
324 // assert(! u.exception());
325 // assert(! t.exception());
326 return static_cast<bool>(t < u) ? t : u;
329 constexpr static r_type max(const r_type & t, const r_type & u){
330 // assert(! u.exception());
331 // assert(! t.exception());
332 return static_cast<bool>(t < u) ? u : t;
336 // union of two intervals
337 // note: we can't use t_interval | u_interval because it
338 // depends on max and min which in turn depend on < which in turn
339 // depends on implicit conversion of tribool to bool
340 constexpr static r_interval_type union_interval(
341 const r_interval_type & t,
342 const r_interval_type & u
344 //const r_type & rl = min(t.l, u.l);
345 const r_type & rmin = static_cast<bool>(t.l < u.l) ? t.l : u.l;
346 //const r_type & ru = max(t.u, u.u);
347 const r_type & rmax = static_cast<bool>(t.u < u.u) ? u.u : t.u;
348 return r_interval_type(rmin, rmax);
351 constexpr static const r_interval_type r_interval =
352 union_interval(t_interval, u_interval);
354 constexpr static auto rl = r_interval.l;
355 constexpr static auto ru = r_interval.u;
357 using type = typename result_type<
360 ? std::numeric_limits<temp_base_type>::min()
361 : static_cast<temp_base_type>(rl),
363 ? std::numeric_limits<temp_base_type>::max()
364 : static_cast<temp_base_type>(ru)
368 ///////////////////////////////////////////////////////////////////////
370 template<typename T, typename U>
371 struct left_shift_result {
372 using temp_base_type = typename std::conditional<
373 std::numeric_limits<T>::is_signed,
378 using r_type = checked_result<temp_base_type>;
379 using r_interval_type = interval<r_type>;
381 constexpr static const r_interval_type t_interval{
382 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
383 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
386 constexpr static const r_interval_type u_interval{
387 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
388 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
391 constexpr static const r_interval_type r_interval =
392 t_interval << u_interval;
394 constexpr static auto rl = r_interval.l;
395 constexpr static auto ru = r_interval.u;
397 using type = typename result_type<
400 ? std::numeric_limits<temp_base_type>::min()
401 : static_cast<temp_base_type>(rl),
403 ? std::numeric_limits<temp_base_type>::max()
404 : static_cast<temp_base_type>(ru)
408 ///////////////////////////////////////////////////////////////////////
409 template<typename T, typename U>
410 struct right_shift_result {
411 using temp_base_type = typename std::conditional<
412 std::numeric_limits<T>::is_signed,
417 using r_type = checked_result<temp_base_type>;
418 using r_interval_type = interval<r_type>;
420 constexpr static const r_interval_type t_interval{
421 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
422 checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
425 constexpr static const r_type u_min
426 = checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min()));
428 constexpr static const r_interval_type u_interval{
432 checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
435 constexpr static const r_interval_type r_interval = t_interval >> u_interval;
437 constexpr static auto rl = r_interval.l;
438 constexpr static auto ru = r_interval.u;
440 using type = typename result_type<
443 ? std::numeric_limits<temp_base_type>::min()
444 : static_cast<temp_base_type>(rl),
446 ? std::numeric_limits<temp_base_type>::max()
447 : static_cast<temp_base_type>(ru)
452 ///////////////////////////////////////////////////////////////////////
453 template<typename T, typename U>
454 struct bitwise_and_result {
455 using type = decltype(
456 typename base_type<T>::type()
457 & typename base_type<U>::type()
460 template<typename T, typename U>
461 struct bitwise_or_result {
462 using type = decltype(
463 typename base_type<T>::type()
464 | typename base_type<U>::type()
467 template<typename T, typename U>
468 struct bitwise_xor_result {
469 using type = decltype(
470 typename base_type<T>::type()
471 ^ typename base_type<U>::type()
479 #endif // BOOST_NUMERIC_AUTOMATIC_HPP