<cmath> Support
Description
The library provides wrappers around the standard <cmath> functions for the non-bounded safe floating-point types f32 and f64.
Each wrapper extracts the underlying float or double, delegates to the corresponding std:: function, and re-checks the result against the float_basis invariant before returning it wrapped in the same safe type.
This means the mathematical functions enforce the same IEEE 754 safety contract as the arithmetic operators described in Floating-Point Types: an exceptional result is turned into an exception rather than being silently propagated.
#include <boost/safe_numbers/cmath.hpp>
The wrappers are constrained to the non-bounded floating-point types, so they accept f32 and f64 only.
They are not provided for bounded_float<Min, Max>, whose invariant (a value inside a compile-time range) is not preserved by transcendental functions.
See Bounded Floating-Point Types.
The functions fall into four groups, distinguished by how they handle exceptional values:
-
Value functions (trigonometric, power, and so on) return a safe floating-point value and throw when the result is not finite.
-
Classification predicates (
isnan,isinf, …) inspect a value, never throw, and are the escape hatch for reasoning about infinities and NaNs. -
Comparison predicates (
isgreater,isless, …) perform non-signaling ordered comparisons and never throw. -
Integer-returning functions (
lround,ilogb, …) return a safe integer type and reject non-finite input.
When CUDA support is enabled (BOOST_SAFE_NUMBERS_ENABLE_CUDA) and the translation unit is compiled by nvcc, the wrappers delegate to cuda::std:: instead of std::, so the same calls work in device code.
See CUDA Support.
Error Handling
The value functions follow the same conventions as the floating-point arithmetic operators. The function is evaluated on the underlying type, and then the result is classified according to IEEE 754:
| Result | Exception |
|---|---|
Finite |
None; the value is returned as the same safe type |
Positive infinity |
|
Negative infinity |
|
NaN (quiet or signaling) |
|
It is the result that is validated, not the input.
A NaN input usually propagates to a NaN result and therefore throws std::domain_error, but the few functions that discard a NaN operand (for example fmin and fmax) do not throw when their finite result is well defined.
Conversely, a finite input that produces a non-finite result (for example pow overflowing, or copysign carrying through an infinity) throws even though the input was ordinary.
using namespace boost::safe_numbers;
auto r = sqrt(f64{4.0}); // r == f64{2.0}
// sqrt(f64{-1.0}); // throws std::domain_error (result is NaN)
// pow(f64{2.0}, f64{2000.0}); // throws std::overflow_error (result is +inf)
// pow(f64{0.0}, f64{-1.0}); // throws std::overflow_error (0 ^ -1 == +inf)
// fmod(f64{1.0}, f64{0.0}); // throws std::domain_error (result is NaN)
auto m = fmax(f64{std::numeric_limits<double>::quiet_NaN()}, f64{3.0}); // m == f64{3.0}, no throw
The classification and comparison predicates never throw; they are noexcept.
The integer-returning functions throw std::domain_error when the input is not finite, since the standard result is otherwise unspecified.
Constexpr Support
The value functions and the classification and comparison predicates are constexpr.
An exceptional result encountered during constant evaluation reaches the throwing branch and is reported as a compile-time error, exactly as for the arithmetic operators.
The integer-returning functions are not constexpr, because the underlying standard functions they delegate to are not constexpr in C20 or C23.
Common Signatures
Apart from the functions with their own signatures listed below, every value function is either a unary or a binary overload with one of these two shapes, where T is f32 or f64:
// Unary value functions (sin, sqrt, exp, ...)
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto fn(const T x) -> T;
// Binary value functions (atan2, pow, fmod, ...)
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto fn(const T x, const T y) -> T;
Trigonometric Functions
| Function | Arity | Description |
|---|---|---|
|
unary |
Sine of |
|
unary |
Cosine of |
|
unary |
Tangent of |
|
unary |
Arc sine, in the range [ |
|
unary |
Arc cosine, in the range [ |
|
unary |
Arc tangent, in the range [ |
|
binary |
Arc tangent of |
Hyperbolic Functions
| Function | Arity | Description |
|---|---|---|
|
unary |
Hyperbolic sine of |
|
unary |
Hyperbolic cosine of |
|
unary |
Hyperbolic tangent of |
|
unary |
Inverse hyperbolic sine of |
|
unary |
Inverse hyperbolic cosine of |
|
unary |
Inverse hyperbolic tangent of |
Exponential and Logarithmic Functions
| Function | Arity | Description |
|---|---|---|
|
unary |
Base-e exponential, |
|
unary |
Base-2 exponential, |
|
unary |
|
|
unary |
Natural (base-e) logarithm |
|
unary |
Base-2 logarithm |
|
unary |
Base-10 logarithm |
|
unary |
|
|
unary |
Floating-point base-radix exponent of |
Power and Root Functions
| Function | Arity | Description |
|---|---|---|
|
unary |
Square root of |
|
unary |
Cube root of |
|
binary |
|
|
binary |
|
Error and Gamma Functions
| Function | Arity | Description |
|---|---|---|
|
unary |
Error function of |
|
unary |
Complementary error function, |
|
unary |
Gamma function of |
|
unary |
Natural logarithm of the absolute value of the gamma function |
Nearest-Integer Functions
These return a floating-point value (the nearest integer represented as T), not an integer type.
For the variants that return a safe integer type, see Integer-Returning Functions.
| Function | Arity | Description |
|---|---|---|
|
unary |
Largest integer value not greater than |
|
unary |
Smallest integer value not less than |
|
unary |
Nearest integer not greater in magnitude than |
|
unary |
Nearest integer, rounding halfway cases away from zero |
|
unary |
Nearest integer using the current rounding mode, without raising |
|
unary |
Nearest integer using the current rounding mode |
Absolute Value
| Function | Arity | Description |
|---|---|---|
|
unary |
Absolute value of |
|
unary |
Absolute value of |
Remainder, Difference, Minimum, Maximum, and Sign
| Function | Arity | Description |
|---|---|---|
|
binary |
Floating-point remainder of |
|
binary |
IEEE 754 remainder of |
|
binary |
Positive difference: |
|
binary |
The smaller of |
|
binary |
The larger of |
|
binary |
A value with the magnitude of |
|
binary |
The next representable value after |
copysign carries the magnitude of its first argument through unchanged, so copysign(+inf, -1.0) produces negative infinity and therefore throws std::underflow_error.
Likewise fmax and fmin ignore a single NaN, but if their result is an infinity (for example fmax(+inf, 3.0)) the result check still throws.
|
Fused Multiply-Add
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto fma(const T x, const T y, const T z) -> T;
Computes x * y + z as a single operation, with a single rounding step.
Scaling Functions
These scale a floating-point value by a power of two using an integer exponent.
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto ldexp(const T x, const int exp) -> T;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto scalbn(const T x, const int exp) -> T;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto scalbln(const T x, const long exp) -> T;
| Function | Description |
|---|---|
|
Multiplies |
|
Multiplies |
|
As |
A scaled result that overflows to an infinity throws std::overflow_error or std::underflow_error in the same way as the other value functions.
Classification Predicates
These inspect a value and never throw.
They are constexpr and noexcept, and delegate to the library’s own constant-evaluation-friendly classification helpers, so they can be used to reason about infinities and NaNs without triggering an exception.
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isnan(const T x) noexcept -> bool;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isinf(const T x) noexcept -> bool;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isfinite(const T x) noexcept -> bool;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isnormal(const T x) noexcept -> bool;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto signbit(const T x) noexcept -> bool;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto fpclassify(const T x) noexcept -> int;
| Function | Returns | Description |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
One of |
Comparison Predicates
These perform non-signaling ordered comparisons: they return false (rather than throwing) when an operand is NaN.
They are constexpr and noexcept.
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isgreater(const T x, const T y) noexcept -> bool;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isgreaterequal(const T x, const T y) noexcept -> bool;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isless(const T x, const T y) noexcept -> bool;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto islessequal(const T x, const T y) noexcept -> bool;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto islessgreater(const T x, const T y) noexcept -> bool;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isunordered(const T x, const T y) noexcept -> bool;
| Function | Description |
|---|---|
|
|
|
|
|
|
|
|
|
`x < y |
x > y`, and |
|
|
|
Integer-Returning Functions
These return a safe integer type whose width matches the corresponding standard return type.
A non-finite input has an unspecified standard result and raises FE_INVALID, so it is reported as a std::domain_error instead.
They are not constexpr, because the underlying standard functions are not constexpr in C20 or C23.
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] auto lround(const T x) -> i64;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] auto llround(const T x) -> i64;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] auto lrint(const T x) -> i64;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] auto llrint(const T x) -> i64;
template <non_bounded_float_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] auto ilogb(const T x) -> i32;
| Function | Returns | Description |
|---|---|---|
|
|
Rounds |
|
|
As |
|
|
Rounds |
|
|
As |
|
|
Extracts the unbiased base-radix exponent of |
A non-finite input to any of these functions throws std::domain_error.
ilogb additionally rejects a zero argument (whose standard result is FP_ILOGB0), so it throws on NaN, infinity, and zero.
|
Examples
<cmath> functions with the safe floating-point types.// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/safe_numbers/cmath.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <limits>
#include <stdexcept>
int main()
{
using namespace boost::safe_numbers;
std::cout << std::boolalpha;
// Value functions return the same safe floating-point type
std::cout << "sqrt(4.0) = " << sqrt(f64{4.0}) << '\n';
std::cout << "hypot(3, 4) = " << hypot(f64{3.0}, f64{4.0}) << '\n';
std::cout << "pow(2, 10) = " << pow(f64{2.0}, f64{10.0}) << '\n';
std::cout << '\n';
// Classification predicates never throw, even on non-finite values
const f64 inf {std::numeric_limits<double>::infinity()};
const f64 nan {std::numeric_limits<double>::quiet_NaN()};
std::cout << "isinf(inf) = " << isinf(inf) << '\n';
std::cout << "isnan(nan) = " << isnan(nan) << '\n';
std::cout << "isfinite(1.0) = " << isfinite(f64{1.0}) << '\n';
std::cout << '\n';
// Non-signaling comparisons are false when an operand is NaN
std::cout << "isless(1, 2) = " << isless(f64{1.0}, f64{2.0}) << '\n';
std::cout << "isless(nan, 2) = " << isless(nan, f64{2.0}) << '\n';
std::cout << '\n';
// Integer-returning functions produce a safe integer type
std::cout << "lround(2.5) = " << lround(f64{2.5}) << '\n';
std::cout << "ilogb(8.0) = " << ilogb(f64{8.0}) << '\n';
std::cout << '\n';
// Exceptional results raise an exception instead of propagating
try
{
const auto bad {sqrt(f64{-1.0})};
static_cast<void>(bad);
}
catch (const std::domain_error&)
{
std::cout << "sqrt(-1.0) threw std::domain_error\n";
}
try
{
const auto big {pow(f64{2.0}, f64{2000.0})};
static_cast<void>(big);
}
catch (const std::overflow_error&)
{
std::cout << "pow(2, 2000) threw std::overflow_error\n";
}
return 0;
}
Output:
sqrt(4.0) = 2 hypot(3, 4) = 5 pow(2, 10) = 1024 isinf(inf) = true isnan(nan) = true isfinite(1.0) = true isless(1, 2) = true isless(nan, 2) = false lround(2.5) = 3 ilogb(8.0) = 3 sqrt(-1.0) threw std::domain_error pow(2, 2000) threw std::overflow_error