Skip to main content

Boost.NumericConversion

boost::numeric_cast<Target>(source) is a checked numeric conversion that throws boost::numeric::bad_numeric_cast (or a subclass) when the value does not fit in the target type. It replaces static_cast<int>(some_double) — which silently truncates, wraps, or loses precision — with a conversion that either succeeds exactly or fails loudly.

The problem it solves

C++ implicit and static_cast numeric conversions are silent: assigning a long long to an int wraps on overflow, converting a negative value to unsigned wraps modulo 2^N, and casting a large double to int is undefined behaviour. These bugs are hard to find because the code compiles without warning and "works" until the value is out of range. numeric_cast makes every such conversion a checked, documented decision point.

Basic usage

numeric_cast.cpp
#include <boost/numeric/conversion/cast.hpp>
#include <iostream>

int main() {
long long big = 42;

// Safe: 42 fits in int
int a = boost::numeric_cast<int>(big);
std::cout << a << "\n"; // 42

// Overflow: throws boost::numeric::positive_overflow
big = 3'000'000'000LL;
try {
int b = boost::numeric_cast<int>(big);
(void)b;
} catch (const boost::numeric::positive_overflow& e) {
std::cout << "overflow: " << e.what() << "\n";
}
}

What it catches

Conversionstatic_castnumeric_cast
long long 3B to intsilent wrapthrows positive_overflow
-1 to unsigned intwraps to UINT_MAXthrows negative_overflow
1e20 (double) to intundefined behaviourthrows positive_overflow
3.14 to inttruncates to 3throws (loss of fractional part)
42 to doubleexactsucceeds
signed_unsigned.cpp
#include <boost/numeric/conversion/cast.hpp>
#include <iostream>

int main() {
int neg = -5;

try {
unsigned int u = boost::numeric_cast<unsigned int>(neg);
(void)u;
} catch (const boost::numeric::negative_overflow& e) {
std::cout << "caught: " << e.what() << "\n";
}
}
static_cast is not a safe alternative

static_cast<int>(3'000'000'000LL) compiles and runs — it produces a garbage value. There is no warning, no exception, no signal. This class of bug is responsible for real-world failures (Ariane 5, integer overflow CVEs). numeric_cast exists to make this impossible.

Exception hierarchy

All exceptions derive from boost::numeric::bad_numeric_cast, which itself derives from std::bad_cast:

You can catch the base bad_numeric_cast for any failure, or catch positive_overflow / negative_overflow specifically.

The converter class template

For advanced use, boost::numeric::converter<Target, Source, Traits, OverflowHandler, ...> lets you customise overflow behaviour without exceptions — for instance, clamping to the target's min/max instead of throwing.

clamping.cpp
#include <boost/numeric/conversion/converter.hpp>
#include <iostream>
#include <limits>

struct clamp_overflow {
static int handle(double value) {
if (value > 0) return std::numeric_limits<int>::max();
return std::numeric_limits<int>::min();
}
};

int main() {
// Custom converter that clamps instead of throwing
using clamped = boost::numeric::converter<int, double,
boost::numeric::conversion_traits<int, double>,
boost::numeric::def_overflow_handler // default: throw
>;

// Default converter throws on overflow
try {
int v = clamped::convert(1e18);
(void)v;
} catch (...) {
std::cout << "overflow caught\n";
}
}
Use numeric_cast for boundaries

numeric_cast is most valuable at system boundaries: user input, network protocol fields, file format values, and API return values. Interior arithmetic in a tight loop rarely needs it — the cost of the range check matters there, and the values should already be validated.

Compile-time traits

boost::numeric::bounds<T> and boost::numeric::conversion_traits<Target, Source> provide compile-time information about type ranges and conversion categories:

traits.cpp
#include <boost/numeric/conversion/bounds.hpp>
#include <boost/numeric/conversion/conversion_traits.hpp>
#include <iostream>

int main() {
std::cout << "int max: " << boost::numeric::bounds<int>::highest() << "\n";
std::cout << "int min: " << boost::numeric::bounds<int>::lowest() << "\n";

using traits = boost::numeric::conversion_traits<int, double>;
// traits::supertype, traits::subtype, etc.
}

numeric_cast versus alternatives

ApproachOverflow detectionExceptionCompile-timeNotes
static_castnononosilent truncation/wrap
boost::numeric_castyesyesnoheader-only, well-tested
C++20 std::in_rangepartialno (returns bool)noonly for integer-to-integer
gsl::narrow_castnononodocumentation-only cast
gsl::narrowyesyesnosimilar to numeric_cast
No standard equivalent

There is no std::numeric_cast in the C++ standard. C++20 added std::in_range for querying whether a value fits in an integer type, but it does not perform the conversion itself. For a throwing checked cast, boost::numeric_cast remains the standard approach.

See also