Overflow Policies
Description
The library provides multiple overflow handling policies for arithmetic and shift operations.
The default arithmetic operators (+, -, *, /, %) and shift operators (<<, >>) use the throw_exception policy, but alternative free functions and a generic policy-parameterized interface allow selecting different behavior at the call site.
The overflow_policy Enum
#include <boost/safe_numbers/overflow_policy.hpp>
namespace boost::safe_numbers {
enum class overflow_policy
{
throw_exception, // Throw an exception on overflow/underflow
saturate, // Clamp to the representable range
overflow_tuple, // Wrap and return a flag indicating overflow
checked, // Return std::nullopt on overflow/underflow
wrapping, // Wrap silently
strict, // Call std::exit(EXIT_FAILURE) on error
widen, // Promote to the next wider type (add/mul only)
};
} // namespace boost::safe_numbers
Policy Summary
| Policy | Overflow/Underflow Behavior | Division by Zero | noexcept |
|---|---|---|---|
|
Throws exception |
Throws |
No |
|
Clamps to min/max |
Throws |
Add/Sub/Mul: Yes, Div/Mod: No |
|
Wraps, returns flag |
Throws |
Add/Sub/Mul: Yes, Div/Mod: No |
|
Returns |
Returns |
Yes |
|
Wraps silently |
Throws |
Add/Sub/Mul: Yes, Div/Mod: No |
|
Calls |
Calls |
Yes |
|
Promotes to next wider type (add/mul only) |
N/A (only add/mul supported) |
Yes |
Named Arithmetic Functions
For cases where throwing exceptions is not desired, named free functions are provided for each policy.
Saturating Arithmetic
template <UnsignedLibType T>
constexpr T saturating_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T saturating_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T saturating_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T saturating_div(T lhs, T rhs);
template <UnsignedLibType T>
constexpr T saturating_mod(T lhs, T rhs);
These functions clamp the result to the representable range instead of throwing:
-
saturating_add: Returns the sum, saturating atstd::numeric_limits<T>::max()on overflow -
saturating_sub: Returns the difference, saturating atstd::numeric_limits<T>::min()(zero) on underflow -
saturating_mul: Returns the product, saturating atstd::numeric_limits<T>::max()on overflow -
saturating_div: Returns the quotient; throwsstd::domain_erroron division by zero (overflow is impossible) -
saturating_mod: Returns the remainder; throwsstd::domain_erroron division by zero (overflow is impossible)
Overflowing Arithmetic
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_div(T lhs, T rhs);
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_mod(T lhs, T rhs);
These functions provide well-defined wrapping semantics with a flag to indicate if overflow occurred.
This follows normal C family unsigned rollover where UINT_MAX + 1 == 0 and 0 - 1 == UINT_MAX.
-
overflowing_add: Returns the wrapped sum andtrueif overflow occurred -
overflowing_sub: Returns the wrapped difference andtrueif underflow occurred -
overflowing_mul: Returns the wrapped product andtrueif overflow occurred -
overflowing_div: Returns the quotient andfalse; throwsstd::domain_erroron division by zero -
overflowing_mod: Returns the remainder andfalse; throwsstd::domain_erroron division by zero
Checked Arithmetic
template <UnsignedLibType T>
constexpr std::optional<T> checked_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_div(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_mod(T lhs, T rhs) noexcept;
These functions return std::nullopt on overflow, underflow, or division by zero:
-
checked_add: Returns the sum, orstd::nullopton overflow -
checked_sub: Returns the difference, orstd::nullopton underflow -
checked_mul: Returns the product, orstd::nullopton overflow -
checked_div: Returns the quotient, orstd::nullopton division by zero -
checked_mod: Returns the remainder, orstd::nullopton division by zero
Wrapping Arithmetic
template <UnsignedLibType T>
constexpr T wrapping_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T wrapping_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T wrapping_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T wrapping_div(T lhs, T rhs);
template <UnsignedLibType T>
constexpr T wrapping_mod(T lhs, T rhs);
These functions wrap on overflow without any indication:
-
wrapping_add: Returns the wrapped sum -
wrapping_sub: Returns the wrapped difference -
wrapping_mul: Returns the wrapped product -
wrapping_div: Returns the quotient; throwsstd::domain_erroron division by zero -
wrapping_mod: Returns the remainder; throwsstd::domain_erroron division by zero
Strict Arithmetic
template <UnsignedLibType T>
constexpr T strict_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_div(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_mod(T lhs, T rhs) noexcept;
These functions call std::exit(EXIT_FAILURE) on error, providing a hard termination policy for safety-critical applications where exceptions cannot be used:
-
strict_add: Returns the sum; callsstd::exit(EXIT_FAILURE)on overflow -
strict_sub: Returns the difference; callsstd::exit(EXIT_FAILURE)on underflow -
strict_mul: Returns the product; callsstd::exit(EXIT_FAILURE)on overflow -
strict_div: Returns the quotient; callsstd::exit(EXIT_FAILURE)on division by zero -
strict_mod: Returns the remainder; callsstd::exit(EXIT_FAILURE)on modulo by zero
All strict functions are marked noexcept since std::exit does not throw.
Widening Arithmetic
template <UnsignedLibType T>
constexpr auto widening_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr auto widening_mul(T lhs, T rhs) noexcept;
These functions avoid overflow entirely by promoting the result to the next wider unsigned integer type.
The promotion chain is: uint8 → u16, u16 → u32, u32 → u64, u64 → uint128.
Since uint128 is the widest supported type, widening is not available for uint128 operands (a static_assert fires).
Only addition and multiplication are provided because subtraction, division, and modulo cannot overflow into a range that requires a wider type.
-
widening_add: Returns the sum in the next wider type -
widening_mul: Returns the product in the next wider type
Both functions are noexcept.
Named Shift Functions
The same policy variants available for arithmetic operations are also available for shift operations.
The widen policy is not supported for shifts.
Overflow Conditions
-
Left shift (
<<): Overflow occurs whenbit_width(lhs) + rhs >= std::numeric_limits<BasisType>::digits(i.e., bits would be shifted past the type width). -
Right shift (
>>): Overflow occurs whenrhs >= std::numeric_limits<BasisType>::digits(i.e., the shift amount is greater than or equal to the type width).
Saturating Shifts
template <UnsignedLibType T>
constexpr T saturating_shl(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T saturating_shr(T lhs, T rhs) noexcept;
-
saturating_shl: Returns the shifted value, saturating atstd::numeric_limits<T>::max()on overflow -
saturating_shr: Returns the shifted value, saturating at0when the shift amount is >= the type width
Overflowing Shifts
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_shl(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_shr(T lhs, T rhs) noexcept;
-
overflowing_shl: Returns the wrapped shifted value andtrueif overflow occurred -
overflowing_shr: Returns0andtrueif the shift amount is >= the type width; otherwise the shifted value andfalse
Checked Shifts
template <UnsignedLibType T>
constexpr std::optional<T> checked_shl(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_shr(T lhs, T rhs) noexcept;
-
checked_shl: Returns the shifted value, orstd::nullopton overflow -
checked_shr: Returns the shifted value, orstd::nulloptwhen the shift amount is >= the type width
Wrapping Shifts
template <UnsignedLibType T>
constexpr T wrapping_shl(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T wrapping_shr(T lhs, T rhs) noexcept;
-
wrapping_shl: Performs the shift, allowing bits to be lost without indication -
wrapping_shr: Returns0when the shift amount is >= the type width; otherwise performs the shift normally
Strict Shifts
template <UnsignedLibType T>
constexpr T strict_shl(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_shr(T lhs, T rhs) noexcept;
-
strict_shl: Returns the shifted value; callsstd::exit(EXIT_FAILURE)on overflow -
strict_shr: Returns the shifted value; callsstd::exit(EXIT_FAILURE)when the shift amount is >= the type width
All shift policy functions are noexcept.
Generic Policy-Parameterized Arithmetic
template <overflow_policy Policy, UnsignedLibType T>
constexpr auto add(T lhs, T rhs);
template <overflow_policy Policy, UnsignedLibType T>
constexpr auto sub(T lhs, T rhs);
template <overflow_policy Policy, UnsignedLibType T>
constexpr auto mul(T lhs, T rhs);
template <overflow_policy Policy, UnsignedLibType T>
constexpr auto div(T lhs, T rhs);
template <overflow_policy Policy, UnsignedLibType T>
constexpr auto mod(T lhs, T rhs);
template <overflow_policy Policy, UnsignedLibType T>
constexpr auto shl(T lhs, T rhs);
template <overflow_policy Policy, UnsignedLibType T>
constexpr auto shr(T lhs, T rhs);
These functions accept an overflow_policy as a template parameter and dispatch to the corresponding named function.
The widen policy is not supported for shl or shr.
The return type depends on the policy:
| Policy | Return Type |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
Next wider unsigned integer type (add/mul only) |
This allows writing generic code parameterized on the overflow policy:
using namespace boost::safe_numbers;
// The policy can be a template parameter of your own function
template <overflow_policy Policy>
auto compute(u32 a, u32 b)
{
return add<Policy>(a, b);
}
auto result_sat = compute<overflow_policy::saturate>(u32{100}, u32{200});
auto result_chk = compute<overflow_policy::checked>(u32{100}, u32{200});
Exception Summary
The default operators and some named functions throw exceptions on error:
| Operation | Exception Type | Condition |
|---|---|---|
|
|
Result exceeds maximum value |
|
|
Result would be negative |
|
|
Result exceeds maximum value |
|
|
Division by zero |
|
|
Modulo by zero |
|
|
Value is at maximum |
|
|
Value is zero |
|
|
|
|
|
|
|
|
Division by zero |
|
|
Division by zero |
|
|
Division by zero |