UTL

Collection of self-contained header-only libraries for C++17

View on GitHub

utl::math

<- to README.md

<- to implementation.hpp

utl::math header is a collection of mathematical utils that includes:

All implementations template, constexpr and noexcept.

[!Note] For a more “complete” mathematical library check out DmitriBogdanov/GSE.

Definitions

// Constants
namespace constants {
    constexpr double pi      = 3.14159265358979323846;
    constexpr double two_pi  = 2.0 * pi;
    constexpr double half_pi = 0.5 * pi;
    constexpr double inv_pi  = 1.0 / pi;
    constexpr double e       = 2.71828182845904523536;
    constexpr double phi     = 1.61803398874989484820;
}

// Basic functions
template <class T> constexpr T       abs(T x) noexcept;
template <class T> constexpr T      sign(T x) noexcept;
template <class T> constexpr T     bsign(T x) noexcept;
template <class T> constexpr T       sqr(T x) noexcept;
template <class T> constexpr T      cube(T x) noexcept;
template <class T> constexpr T       inv(T x) noexcept;
template <class T> constexpr T heaviside(T x) noexcept;

// Non-overflowing functions
template <class T> constexpr T midpoint(T a, T b) noexcept;
template <class T> constexpr T  absdiff(T a, T b) noexcept;

// Power functions
template <class T, class U> constexpr T     pow(T x, U p) noexcept;
template <         class U> constexpr U signpow(     U p) noexcept;

// Index functions
template <class T> constexpr T kronecker_delta(T i, T j     ) noexcept;
template <class T> constexpr T     levi_civita(T i, T j, T k) noexcept;

// Conversions
template <class T> constexpr T deg_to_rad(T degrees) noexcept;
template <class T> constexpr T rad_to_deg(T radians) noexcept;

// Sequence operations
template <class Idx, class Func> constexpr auto  sum(Idx low, Idx high, Func&& func);
template <class Idx, class Func> constexpr auto prod(Idx low, Idx high, Func&& func);

[!Note] All methods have appropriate SFINAE-restrictions, which are omitted in documentation to reduce verbosity.

Methods

Constants

namespace constants {
    constexpr double pi      = 3.14159265358979323846;
    constexpr double two_pi  = 2.0 * pi;
    constexpr double half_pi = 0.5 * pi;
    constexpr double inv_pi  = 1.0 / pi;
    constexpr double e       = 2.71828182845904523536;
    constexpr double phi     = 1.61803398874989484820;
}

Basic mathematical constants. In C++20 most of these get standardized as a part of <numbers> header.

Basic functions

template <class T> constexpr T       abs(T x) noexcept;
template <class T> constexpr T      sign(T x) noexcept;
template <class T> constexpr T     bsign(T x) noexcept;
template <class T> constexpr T       sqr(T x) noexcept;
template <class T> constexpr T      cube(T x) noexcept;
template <class T> constexpr T       inv(T x) noexcept;
template <class T> constexpr T heaviside(T x) noexcept;
Returns $ x $, $\mathrm{sign} (x)$, $x^2$, $x^3$, $x^{-1}$, or $H(x)$.

T can be of any arithmetic type except bool.

To reduce integer division surprises, inv() also requires a floating point.

Note 1: sign() is a standard sign function which returns $-1$, $0$ or $1$. bsign() is a binary variation that returns $1$ instead of $0$.

Note 2: $H(x)$ refers to Heaviside step function.

Non-overflowing functions

template <class T> constexpr T midpoint(T a, T b) noexcept;
template <class T> constexpr T  absdiff(T a, T b) noexcept;
Computes $\dfrac{a + b}{2}$ or $ a - b $ without potential overflow.

T can be of any arithmetic type except bool.

Integer midpoint() rounds down in case of a fractional result.

Note 1: Correctly computing integer / float midpoint() is trickier than it might initially seem, naive (a + b) / 2 formula suffers multiple issues when applied to values near min / max of the given type.

Note 2: Compared to integer std::midpoint() (which rounds towards a), integer math::midpoint() (which rounds down) is commutative and faster to compute. Floating point behavior is the same.

Power functions

template <class T, class U> constexpr T     pow(T x, U p) noexcept;
template <         class U> constexpr U signpow(     U p) noexcept;

Returns $x^p$ or $-1^p$ .

T can be of any arithmetic type except bool.

U can be of any integral type.

Note: Computing signpow(p) is significantly faster than std::pow(-1, p).

Index functions

template <class T> constexpr T kronecker_delta(T i, T j) noexcept;

Computes Kronecker delta symbol: 1 if i == j, 0 otherwise.

T can be of any integral type.

template <class T> constexpr T levi_civita(T i, T j, T k) noexcept;

Computes Levi-Civita symbol: 1 if (i, j, k) form an even permutation, -1 if (i, j, k) form an odd permutation, and 0 if any the adjacent letters are equal.

T can be of any integral type.

Conversions

template <class T> constexpr T deg_to_rad(T degrees) noexcept;
template <class T> constexpr T rad_to_deg(T radians) noexcept;

Converts degrees to radians and back.

T can be of any floating point type.

Sequence operations

template <class Idx, class Func> constexpr auto  sum(Idx low, Idx high, Func&& f);
template <class Idx, class Func> constexpr auto prod(Idx low, Idx high, Func&& f);

Computes $\sum_{i = low}^{high} f(i)$ or $\prod_{i = low}^{high} f(i)$.

Idx can be of any integral type.

noexcept when f is non-throwing.

Examples

Template math functions

[ Run this code ]

using namespace utl;

static_assert( math::abs      (-7 ) ==  7  );
static_assert( math::sign     ( 0 ) ==  0  );
static_assert( math::bsign    ( 0 ) ==  1  );
static_assert( math::sqr      (-2 ) ==  4  );
static_assert( math::cube     (-2 ) == -8  );
static_assert( math::inv      ( 2.) == 0.5 );
static_assert( math::heaviside( 2.) ==  1  );

static_assert( math::midpoint(3, 5) == 4 );
static_assert( math::absdiff (4, 7) == 3 );

static_assert( math::pow    (2, 7) ==  128 );
static_assert( math::signpow(  -5) == -1   );

Index functions

[ Run this code ]

using namespace utl;

static_assert( math::kronecker_delta(3, 4) == 0 );
static_assert( math::kronecker_delta(3, 3) == 1 );

static_assert( math::levi_civita(3, 4, 4) ==  0 );
static_assert( math::levi_civita(3, 4, 5) ==  1 );
static_assert( math::levi_civita(5, 4, 3) == -1 );

Conversions

[ Run this code ]

using namespace utl;

static_assert( math::absdiff(math::deg_to_rad(180.), math::constants::pi) < 1e-16  );

static_assert( math::absdiff(math::rad_to_deg(math::constants::pi), 180.) < 1e-16  );

Summation & product

[ Run this code ]

using namespace utl;

constexpr auto  sum = math::sum( 1, 3, [](int i){ return math::sqr(i); }); // 1^2 + 2^2 + 3^2
constexpr auto prod = math::prod(1, 3, [](int i){ return math::sqr(i); }); // 1^2 * 2^2 * 3^2

static_assert(  sum == 1 + 4 + 9 );
static_assert( prod == 1 * 4 * 9 );

Newer standards

Some parts of this header can be replaced with std features in newer standards: