Signed Integer Types
Description
The library provides safe signed integer types that detect overflow, underflow, division by zero, and other undefined behavior at runtime. These types are drop-in replacements for the standard signed integer types with added safety guarantees.
| Type | Underlying Type | Width | Min | Max |
|---|---|---|---|---|
|
|
8 bits |
-128 |
127 |
|
|
16 bits |
-32,768 |
32,767 |
|
|
32 bits |
-2,147,483,648 |
2,147,483,647 |
|
|
64 bits |
-9,223,372,036,854,775,808 |
9,223,372,036,854,775,807 |
|
|
128 bits |
-170,141,183,460,469,231,731,687,303,715,884,105,728 |
170,141,183,460,469,231,731,687,303,715,884,105,727 |
Each type exposes a basis_type member type alias that refers to the underlying integer type, allowing conversion back to built-in types when needed.
#include <boost/safe_numbers/signed_integers.hpp>
namespace boost::safe_numbers {
using i8 = detail::signed_integer_basis<std::int8_t>;
using i16 = detail::signed_integer_basis<std::int16_t>;
using i32 = detail::signed_integer_basis<std::int32_t>;
using i64 = detail::signed_integer_basis<std::int64_t>;
using i128 = detail::signed_integer_basis<int128::int128_t>;
template <signed_integral BasisType>
class signed_integer_basis {
public:
using basis_type = BasisType;
// Construction
constexpr signed_integer_basis() noexcept = default;
explicit constexpr signed_integer_basis(BasisType val) noexcept;
template <typename T>
requires std::is_same_v<T, bool>
explicit constexpr signed_integer_basis(T) noexcept = delete; // bool prohibited
// Conversion to underlying types
template <signed_integral OtherBasis>
explicit constexpr operator OtherBasis() const;
// Comparison operators
friend constexpr auto operator<=>(signed_integer_basis lhs, signed_integer_basis rhs) noexcept
-> std::strong_ordering = default;
// Unary operators
constexpr auto operator+() const noexcept -> signed_integer_basis;
constexpr auto operator-() const -> signed_integer_basis;
// Compound assignment operators (arithmetic)
template <signed_integral OtherBasis>
constexpr auto operator+=(signed_integer_basis<OtherBasis> rhs) -> signed_integer_basis&;
template <signed_integral OtherBasis>
constexpr auto operator-=(signed_integer_basis<OtherBasis> rhs) -> signed_integer_basis&;
template <signed_integral OtherBasis>
constexpr auto operator*=(signed_integer_basis<OtherBasis> rhs) -> signed_integer_basis&;
template <signed_integral OtherBasis>
constexpr auto operator/=(signed_integer_basis<OtherBasis> rhs) -> signed_integer_basis&;
template <signed_integral OtherBasis>
constexpr auto operator%=(signed_integer_basis<OtherBasis> rhs) -> signed_integer_basis&;
// Increment and decrement operators
constexpr auto operator++() -> signed_integer_basis&;
constexpr auto operator++(int) -> signed_integer_basis;
constexpr auto operator--() -> signed_integer_basis&;
constexpr auto operator--(int) -> signed_integer_basis;
}; // class signed_integer_basis
// Arithmetic operators (throw on overflow/underflow)
template <signed_integral BasisType>
constexpr auto operator+(signed_integer_basis<BasisType> lhs,
signed_integer_basis<BasisType> rhs) -> signed_integer_basis<BasisType>;
template <signed_integral BasisType>
constexpr auto operator-(signed_integer_basis<BasisType> lhs,
signed_integer_basis<BasisType> rhs) -> signed_integer_basis<BasisType>;
template <signed_integral BasisType>
constexpr auto operator*(signed_integer_basis<BasisType> lhs,
signed_integer_basis<BasisType> rhs) -> signed_integer_basis<BasisType>;
template <signed_integral BasisType>
constexpr auto operator/(signed_integer_basis<BasisType> lhs,
signed_integer_basis<BasisType> rhs) -> signed_integer_basis<BasisType>;
template <signed_integral BasisType>
constexpr auto operator%(signed_integer_basis<BasisType> lhs,
signed_integer_basis<BasisType> rhs) -> signed_integer_basis<BasisType>;
} // namespace boost::safe_numbers
Increment and Decrement Operators
constexpr auto operator++() -> signed_integer_basis&;
constexpr auto operator++(int) -> signed_integer_basis;
constexpr auto operator--() -> signed_integer_basis&;
constexpr auto operator--(int) -> signed_integer_basis;
-
++(pre/post): Throwsstd::overflow_errorif the value is already atstd::numeric_limits<BasisType>::max() -
--(pre/post): Throwsstd::underflow_errorif the value is already atstd::numeric_limits<BasisType>::min()
Unary Operators
constexpr auto operator+() const noexcept -> signed_integer_basis;
constexpr auto operator-() const -> signed_integer_basis;
-
+: Returns a copy of the value (identity). -
-: Throwsstd::domain_errorif the value isstd::numeric_limits<BasisType>::min(), since negating the minimum value of a two’s complement signed integer overflows.
Mixed-Width Operations
Operations between different width safe signed integer types are compile-time errors. The operands must be promoted to a common type explicitly before performing the operation.
auto a = i16{100};
auto b = i32{200};
// auto result = a + b; // Compile error: mismatched types
auto result = static_cast<i32>(a) + b; // OK: explicit promotion
Policy-Based Arithmetic Functions
In addition to the default operators (which throw on overflow/underflow), signed integer types support the same policy-based free functions as unsigned types. See Overflow Policies for a detailed description of each overflow policy.
Saturating Arithmetic
Saturating operations clamp the result to std::numeric_limits<T>::max() on overflow or std::numeric_limits<T>::min() on underflow, rather than throwing.
template <SignedLibType T> constexpr T saturating_add(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr T saturating_sub(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr T saturating_mul(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr T saturating_div(T lhs, T rhs); // throws on div-by-zero
template <SignedLibType T> constexpr T saturating_mod(T lhs, T rhs); // throws on mod-by-zero
Division by zero always throws std::domain_error, even with the saturating policy. The min / -1 overflow case saturates to max(). The min % -1 case returns 0.
|
Overflowing Arithmetic
Overflowing operations return a std::pair<T, bool> where the bool indicates whether overflow or underflow occurred. The result wraps on overflow (two’s complement behavior).
template <SignedLibType T> constexpr std::pair<T, bool> overflowing_add(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr std::pair<T, bool> overflowing_sub(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr std::pair<T, bool> overflowing_mul(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr std::pair<T, bool> overflowing_div(T lhs, T rhs); // throws on div-by-zero
template <SignedLibType T> constexpr std::pair<T, bool> overflowing_mod(T lhs, T rhs); // throws on mod-by-zero
Division by zero still throws std::domain_error. The min / -1 case returns (min, true). The min % -1 case returns (0, true).
|
Checked Arithmetic
Checked operations return std::optional<T>. If the operation would overflow, underflow, or divide by zero, std::nullopt is returned.
template <SignedLibType T> constexpr std::optional<T> checked_add(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr std::optional<T> checked_sub(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr std::optional<T> checked_mul(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr std::optional<T> checked_div(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr std::optional<T> checked_mod(T lhs, T rhs) noexcept;
All checked operations are noexcept. Division by zero and min / -1 both return std::nullopt.
|
Strict Arithmetic
Strict operations call std::exit(EXIT_FAILURE) on overflow, underflow, or (for div/mod) division by zero.
template <SignedLibType T> constexpr T strict_add(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr T strict_sub(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr T strict_mul(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr T strict_div(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr T strict_mod(T lhs, T rhs) noexcept;
Widening Arithmetic
Widening operations return the result in the next wider signed integer type, guaranteeing no overflow. Only available for addition and multiplication.
template <SignedLibType T> constexpr auto widening_add(T lhs, T rhs) noexcept;
template <SignedLibType T> constexpr auto widening_mul(T lhs, T rhs) noexcept;
Widening is not supported for i128 (there is no wider signed type). Attempting to use it with i128 is a compile-time error.
|
Generic Policy-Parameterized Arithmetic
These functions accept an overflow_policy template parameter and dispatch to the corresponding named function above.
template <overflow_policy Policy, SignedLibType T> constexpr auto add(T lhs, T rhs);
template <overflow_policy Policy, SignedLibType T> constexpr auto sub(T lhs, T rhs);
template <overflow_policy Policy, SignedLibType T> constexpr auto mul(T lhs, T rhs);
template <overflow_policy Policy, SignedLibType T> constexpr auto div(T lhs, T rhs);
template <overflow_policy Policy, SignedLibType T> constexpr auto mod(T lhs, T rhs);