Overflow Policies
Description
The library provides multiple overflow handling policies for arithmetic operations.
The default arithmetic 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
};
} // 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 |
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.
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);
These functions accept an overflow_policy as a template parameter and dispatch to the corresponding named function.
The return type depends on the policy:
| Policy | Return Type |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
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 |