Math functions¶
We provide many common mathematical functions out of the box. If you think we’re missing a particular math function, and you’d like to see it added, reach out to us and ask!
General usage advice¶
Prefer to make unqualified calls to these functions. So for example: if you’re using unit types
and you want the “max”, just write plain max(...).
- Don’t write
std::max(...), because that would give the wrong function. - Don’t write
au::max(...), because that’s neither necessary nor idiomatic.
Warning
For some functions, including min, max, and clamp, this advice is mandatory in many
cases, such as when the arguments have the same type.
Function categories¶
Here are the functions we provide, grouped roughly into related categories.
Sign-based functions:¶
Checking signs, comparing to 0¶
Quantity cannot be compared to 0 or 0.0, since these are raw numeric types. However, any
Quantity can be compared to ZERO, which is a built-in constant of the library. See our
Zero discussion for more background.
abs¶
Adapts std::abs to Quantity types. Covers both
integral and floating
point overloads of std::abs.
Signature:
Returns: The input quantity, but with std::abs applied to its underlying value.
copysign¶
Adapts std::copysign to
Quantity types.
Signatures:
// 1: First argument Quantity, second argument raw numeric
template <typename U, typename R, typename T>
constexpr auto copysign(Quantity<U, R> mag, T sgn);
// 2: First argument raw numeric, second argument Quantity
template <typename T, typename U, typename R>
constexpr auto copysign(T mag, Quantity<U, R> sgn);
// 3: Both arguments Quantity
template <typename U1, typename R1, typename U2, typename R2>
constexpr auto copysign(Quantity<U1, R1> mag, Quantity<U2, R2> sgn);
Returns: The first argument, with the sign from the second argument applied to it.
Comparison-based functions¶
min, max¶
Select the smaller (min) or larger (max) of the two inputs. This operation is unit-aware, and
supports mixing different input units, as long as they have the same dimension. These functions
support both Quantity and QuantityPoint inputs.
Signatures:1
//
// min()
//
// 1. `Quantity` inputs
template <typename U1, typename U2, typename R1, typename R2>
auto min(Quantity<U1, R1> q1, Quantity<U2, R2> q2);
// 2. `QuantityPoint` inputs
template <typename U1, typename U2, typename R1, typename R2>
auto min(QuantityPoint<U1, R1> p1, QuantityPoint<U2, R2> p2);
//
// max()
//
// 1. `Quantity` inputs
template <typename U1, typename U2, typename R1, typename R2>
auto max(Quantity<U1, R1> q1, Quantity<U2, R2> q2);
// 2. `QuantityPoint` inputs
template <typename U1, typename U2, typename R1, typename R2>
auto max(QuantityPoint<U1, R1> p1, QuantityPoint<U2, R2> p2);
Returns: The value of the smallest (min) or largest (max) of the inputs, expressed in their
common type.
Note
unlike std::min and std::max, we return by value, not by reference. This is because we
support combining different units. This means the return type will generally be different from
the types of the inputs.
Warning
You must use unqualified calls to min and max in many cases, including the common case
where the arguments have the same type. Write min(a, b), not au::min(a, b): the latter will
frequently result in the right overload not being found.
clamp¶
“Clamp” the first parameter to the range defined by the second and third. This is a unit-aware
analogue to std::clamp, which was introduced
in C++17. However, this version differs in several respects from std::clamp, in order to handle
quantities more effectively. We’ll explain these differences at the end of the clamp section.
Signatures:
// 1. `Quantity` inputs
template <typename UV , typename RV ,
typename ULo, typename RLo,
typename UHi, typename RHi>
constexpr auto clamp(
Quantity<UV , RV > v,
Quantity<ULo, RLo> lo,
Quantity<UHi, RHi> hi);
// 2. `QuantityPoint` inputs
template <typename UV , typename RV ,
typename ULo, typename RLo,
typename UHi, typename RHi>
constexpr auto clamp(
QuantityPoint<UV , RV > v,
QuantityPoint<ULo, RLo> lo,
QuantityPoint<UHi, RHi> hi);
A note on custom comparators
std::clamp includes a four-parameter
version, where the fourth parameter is a custom comparator. std::clamp provides this because
it must support an unknowably wide range of custom types. However, au::clamp only supports
Quantity and QuantityPoint types, whose notions of comparison is unambiguous. Therefore, we
opt to keep the library simple, and omit this four-parameter version.
Returns: The closest quantity (or quantity point) to v which is between lo and hi,
inclusive — that is, greater than or equal to lo, and less than or equal to hi. If lo > hi,
the behaviour is undefined. The return type will be expressed in the appropriate unit and rep;
expand the note below for further details.
Details on the unit and rep for the return type
Comparison is a common-unit operation. We must convert all inputs to their common unit before we compare, and therefore the output must also be expressed in this same common unit.
The rep of the return type will be the common type of the input reps. Specifically, given the
above signatures, it will be std::common_type_t<RV, RLo, RHi>.
The unit of the return type depends on whether we are working with Quantity inputs, or
QuantityPoint.
- For
Quantity, the return type’s unit isCommonUnit<UV, ULo, UHi>. - For
QuantityPoint, the return type’s unit isCommonPointUnit<UV, ULo, UHi>: this is the common point unit, which takes relative origin offsets into account.
Differences from std::clamp
Here are the main changes which stem from handling quantities instead of simple numbers.
-
unlike
std::clamp, we return by value, not by reference. This is because we support combining different units. This means the return type will generally be different from the types of the inputs. -
The return type can be different from the type of
v, because we must express it in the common unit and rep of the input parameter types. -
We do not currently plan to provide the four-parameter overload, unless we get a compelling use case.
Warning
You must use unqualified calls to clamp in many cases, including the common case where the
arguments have the same type. Write clamp(a, b, c), not au::clamp(a, b, c): the latter will
frequently result in the right overload not being found.
Interpolating functions¶
lerp (C++20)¶
Warning
lerp, based on std::lerp, is only available for C++20 and later.
For the special case where t = 0.5, mean (see below) presents an alternative.
Linearly interpolate between two Quantity or QuantityPoint values, based on a parameter t,
such that t=0 corresponds to the first argument, and t=1 corresponds to the second argument.
That is, lerp(a, b, t) is logically equivalent to a + (b - a) * t, but with all of the special
case handling found in std::lerp.
Signatures:
// 1. `Quantity` inputs
template <typename U1, typename R1, typename U2, typename R2, typename T>
constexpr auto lerp(Quantity<U1, R1> a, Quantity<U2, R2> b, T t);
// 2. `QuantityPoint` inputs
template <typename U1, typename R1, typename U2, typename R2, typename T>
constexpr auto lerp(QuantityPoint<U1, R1> a, QuantityPoint<U2, R2> b, T t);
Returns: The value notionally equivalent to a + t(b - a), subject to all of the special case
handling outlined in std::lerp. The return value will be expressed in the common unit of the
units of the inputs a and b.
mean¶
Produce the arithmetic mean of two or more Quantity or QuantityPoint values.
Signatures:
// 1. `Quantity` inputs
template <typename U0, typename R0, typename... Us, typename... Rs>
constexpr auto mean(Quantity<U0, R0> q0, Quantity<Us, Rs>... qs);
// 2. `QuantityPoint` inputs
template <typename U0, typename R0, typename... Us, typename... Rs>
constexpr auto mean(QuantityPoint<U0, R0> p0, QuantityPoint<Us, Rs>... ps);
Returns: The arithmetic mean of all inputs. The return value will be expressed in the common unit of the units of the inputs, and the rep will be the common type of the reps of all inputs.
Note
mean has overlap with lerp: mean(a, b) is similar to lerp(a, b, 0.5). Here is
a comparison table to help you decide which to use.
| Criterion | mean |
lerp |
|---|---|---|
| Number of inputs | 2 or more | Exactly 2 |
| Weights | All equal | Arbitrary |
| Special case handling | Avoids overflows | Delegates to std::lerp, which handles many special cases |
| Approach to integer types | Use integer arithmetic | Delegates to std::lerp, which always converts to floating point |
| C++ version compatibility | All versions of Au (C++14 and later) | C++20 and later |
Exponentiation¶
int_pow¶
Raise a Quantity to an integer power. Since this is an arbitrary-unit
operation, the power applies independently
to the unit and to the value.
If the input has an integral rep (storage type), then the exponent cannot be negative.
Signature:
Returns: A Quantity whose unit is the input unit raised to the given power, and whose value
is the input value raised to the given power.
sqrt, cbrt¶
A unit-aware adaptation of std::sqrt and
std::cbrt. Both the input and output are
Quantity types. Since sqrt and cbrt are arbitrary-unit
operations, the root applies
independently to the unit and to the value.
We mirror std::sqrt and std::cbrt in selecting our output rep. That is to say: the output rep
for sqrt and cbrt will be the return type of std::sqrt or std::cbrt, respectively, when
called with a value of our input rep. For example, if the input quantity has int rep, then the
output will be double.
Signature:
template <typename U, typename R>
auto sqrt(Quantity<U, R> q);
template <typename U, typename R>
auto cbrt(Quantity<U, R> q);
Returns: A Quantity whose unit is the square root (for sqrt) or cube root (for cbrt) of
the input quantity’s unit, and whose value is the square root (for sqrt) or cube root (for cbrt)
of the input quantity’s value.
Trigonometric functions¶
sin, cos, tan¶
The value of the named trigonometric function (\sin, \cos, or \tan), evaluated at an input
Quantity representing an angle.
If called with any Quantity which is not an angle, we produce a hard compiler error.
Signatures:
//
// sin()
//
template <typename U, typename R>
auto sin(Quantity<U, R> q);
//
// cos()
//
template <typename U, typename R>
auto cos(Quantity<U, R> q);
//
// tan()
//
template <typename U, typename R>
auto tan(Quantity<U, R> q);
Returns: The result of converting the input to Radians, and then calling the corresponding STL
function (that is, std::sin() for sin(), and so on).
In converting to radians, we mirror the corresponding STL functions in how we handle the rep. For
floating point rep (float, double, and so on), the return type is the rep. For integral
inputs (int, uint32_t, and so on), we cast to double and return double. See, for instance,
the std::sin documentation.
Example: using angles of integer degrees
This example is taken from a test case in the library.
arcsin, arccos, arctan¶
The standard inverse trigonometric functions, each returning a Quantity of Radians.
Each function corresponds to an STL function, except with arc replaced by a. For example,
arcsin() corresponds to std::asin(), and so on. This library’s functions return
Quantity<Radians, T> whenever the corresponding STL function would return a T.
Their names are slightly different than the corresponding STL functions, because in C++ it’s impermissible to have two functions whose signatures differ only in their return type.
Note
For more flexibility and robustness in dealing with arctangent use cases, see
arctan2 below.
Signatures:
//
// arcsin()
//
template <typename T>
auto arcsin(T x);
//
// arccos()
//
template <typename T>
auto arccos(T x);
//
// arctan()
//
template <typename T>
auto arctan(T x);
Returns: radians(stl_func(x)), where stl_func is the corresponding STL function (that is,
std::acos() for arccos(), and so on).
Example: getting the result in degrees
The fact that we return a Quantity, not a raw number, makes these functions far more flexible
than their STL counterparts. For example, it’s easy to get the result in degrees using fluent,
readable code:
arctan2¶
The two-argument arctangent function, which determines the in-plane angle based on the y and x coordinates of a point in the plane.
arctan2 corresponds to std::atan2, but returns a Quantity of Radians instead of a raw
number.
This two-argument version is more robust than the single-argument version. arctan2(y, x) is
equivalent to arctan(y / x), but it avoids the problems faced by the latter whenever x is zero.
Unlike the other inverse trigonometric functions, which only support raw numeric inputs, arctan2
also supports Quantity inputs. These inputs must have the same dimension, or else we will produce
a hard compiler error. We convert them to their common unit, if necessary, before delegating to
std::atan2.
Signatures:
// 1. Raw numeric inputs.
template <typename T, typename U>
auto arctan2(T y, U x);
// 2. Quantity inputs (must be same dimension).
template <typename U1, typename R1, typename U2, typename R2>
auto arctan2(Quantity<U1, R1> y, Quantity<U2, R2> x);
Returns: radians(std::atan2(y, x)). If the inputs are Quantity types, then instead of
passing y and x, we first convert them to their common unit, and pass their values in that unit.
Rounding functions¶
round_as, round_in¶
Round a Quantity to the nearest integer value, using units that are specified explicitly at the
callsite.
These functions are intended as unit-aware analogues to
std::round. However, we firmly oppose the
idea of providing the same (single-argument) API as std::round for Quantity, because a quantity
has no single well-defined result: it depends on the units. (For example, std::round(height) is
an intrinsically ill-formed concept: what is an “integer height”?)
As with everything else in the library, "as" is a word that means “return a strong type from the
library”, and "in" is a word that means “return a raw number”.
Signatures:
//
// round_as(): return a Quantity or QuantityPoint (depending on the input type)
//
// 1. Unit-only version (including safety checks). Typical callsites look like:
// `round_as(units, quantity)`
// a) For `Quantity` inputs
template <typename RoundingUnits, typename U, typename R>
auto round_as(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename RoundingUnits, typename U, typename R>
auto round_as(RoundingUnits rounding_units, QuantityPoint<U, R> q);
// c) For `Constant` inputs †
template <typename RoundingUnits, typename Unit>
constexpr auto round_as(RoundingUnits rounding_units, Constant<Unit> c);
// 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like:
// `round_as<Type>(units, quantity)`
// a) For `Quantity` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
auto round_as(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
auto round_as(RoundingUnits rounding_units, QuantityPoint<U, R> q);
// (No explicit-rep version for `Constant`: since the result is another `Constant`, no rep is
// needed.)
//
// round_in(): return a raw number
//
// 1. Unit-only version (including safety checks). Typical callsites look like:
// `round_in(units, quantity)`
// a) For `Quantity` inputs
template <typename RoundingUnits, typename U, typename R>
auto round_in(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename RoundingUnits, typename U, typename R>
auto round_in(RoundingUnits rounding_units, QuantityPoint<U, R> q);
// (No unit-only version for `Constant`: since `Constant` has no rep, we need an explicit type.)
// 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like:
// `round_in<Type>(units, quantity)`
// a) For `Quantity` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
auto round_in(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
auto round_in(RoundingUnits rounding_units, QuantityPoint<U, R> q);
// c) For `Constant` inputs †
template <typename OutputRep, typename RoundingUnits, typename Unit>
constexpr auto round_in(RoundingUnits rounding_units, Constant<Unit> c);
† Constant support is subject to compile-time arithmetic
limitations.
Returns: The nearest value, which is an integer in the requested units, to the input.
- For functions ending in
_as, this will be the same Au type category (i.e.,Quantity,QuantityPoint, orConstant) as the input. - For functions ending in
_in, this will be a raw number.
For the “explicit rep” versions, the provided type will be the rep of the return value for _as
functions, and will be the type of the return value for _in functions (since they return raw
numbers).
For “unit-only” versions, the policy for the rep is consistent with
std::round. The output rep (_as
functions) or type (_in functions) is the same as the return type of applying std::round to the
input rep.
Tip
If you need to round a Quantity with integral rep, and you want to stay in the integer domain
or need constexpr compatibility, consider using int_round_in and
int_round_as instead.
ceil_in, ceil_as¶
Round a Quantity up to the smallest integer value which is at least as big as that quantity, using
units that are specified explicitly at the callsite.
These functions are intended as unit-aware analogues to
std::ceil. However, we firmly oppose the
idea of providing the same (single-argument) API as std::ceil for Quantity, because a quantity
has no single well-defined result: it depends on the units. (For example, std::ceil(height) is an
intrinsically ill-formed concept: what is an “integer height”?)
As with everything else in the library, "as" is a word that means “return a strong type from the
library”, and "in" is a word that means “return a raw number”.
Signatures:
//
// ceil_as(): return a Quantity or QuantityPoint (depending on the input type)
//
// 1. Unit-only version (including safety checks). Typical callsites look like:
// `ceil_as(units, quantity)`
// a) For `Quantity` inputs
template <typename RoundingUnits, typename U, typename R>
auto ceil_as(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename RoundingUnits, typename U, typename R>
auto ceil_as(RoundingUnits rounding_units, QuantityPoint<U, R> q);
// c) For `Constant` inputs †
template <typename RoundingUnits, typename Unit>
constexpr auto ceil_as(RoundingUnits rounding_units, Constant<Unit> c);
// 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like:
// `ceil_as<Type>(units, quantity)`
// a) For `Quantity` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
auto ceil_as(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
auto ceil_as(RoundingUnits rounding_units, QuantityPoint<U, R> q);
// (No explicit-rep version for `Constant`: since the result is another `Constant`, no rep is
// needed.)
//
// ceil_in(): return a raw number
//
// 1. Unit-only version (including safety checks). Typical callsites look like:
// `ceil_in(units, quantity)`
// a) For `Quantity` inputs
template <typename RoundingUnits, typename U, typename R>
auto ceil_in(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename RoundingUnits, typename U, typename R>
auto ceil_in(RoundingUnits rounding_units, QuantityPoint<U, R> q);
// (No unit-only version for `Constant`: since `Constant` has no rep, we need an explicit type.)
// 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like:
// `ceil_in<Type>(units, quantity)`
// a) For `Quantity` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
auto ceil_in(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
auto ceil_in(RoundingUnits rounding_units, QuantityPoint<U, R> q);
// c) For `Constant` inputs †
template <typename OutputRep, typename RoundingUnits, typename Unit>
constexpr auto ceil_in(RoundingUnits rounding_units, Constant<Unit> c);
† Constant support is subject to compile-time arithmetic
limitations.
Returns: The smallest value, which is an integer in the requested units, that is at least as large as the input.
- For functions ending in
_as, this will be the same Au type category (i.e.,Quantity,QuantityPoint, orConstant) as the input. - For functions ending in
_in, this will be a raw number.
For the “explicit rep” versions, the provided type will be the rep of the return value for _as
functions, and will be the type of the return value for _in functions (since they return raw
numbers).
For “unit-only” versions, the policy for the rep is consistent with
std::ceil. The output rep (_as
functions) or type (_in functions) is the same as the return type of applying std::ceil to the
input rep.
Tip
If you need to ceil a Quantity with integral rep, and you want to stay in the integer domain
or need constexpr compatibility, consider using int_ceil_in and
int_ceil_as instead.
floor_in, floor_as¶
Round a Quantity down to the largest integer value which is no bigger than that quantity, using
the units that are specified explicitly at the callsite.
These functions are intended as unit-aware analogues to
std::floor. However, we firmly oppose the
idea of providing the same (single-argument) API as std::floor for Quantity, because a quantity
has no single well-defined result: it depends on the units. (For example, std::floor(height) is an
intrinsically ill-formed concept: what is an “integer height”?)
As with everything else in the library, "as" is a word that means “return a strong type from the
library”, and "in" is a word that means “return a raw number”.
Signatures:
//
// floor_as(): return a Quantity or QuantityPoint (depending on the input type)
//
// 1. Unit-only version (including safety checks). Typical callsites look like:
// `floor_as(units, quantity)`
// a) For `Quantity` inputs
template <typename RoundingUnits, typename U, typename R>
auto floor_as(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename RoundingUnits, typename U, typename R>
auto floor_as(RoundingUnits rounding_units, QuantityPoint<U, R> q);
// c) For `Constant` inputs †
template <typename RoundingUnits, typename Unit>
constexpr auto floor_as(RoundingUnits rounding_units, Constant<Unit> c);
// 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like:
// `floor_as<Type>(units, quantity)`
// a) For `Quantity` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
auto floor_as(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
auto floor_as(RoundingUnits rounding_units, QuantityPoint<U, R> q);
// (No explicit-rep version for `Constant`: since the result is another `Constant`, no rep is
// needed.)
//
// floor_in(): return a raw number
//
// 1. Unit-only version (including safety checks). Typical callsites look like:
// `floor_in(units, quantity)`
// a) For `Quantity` inputs
template <typename RoundingUnits, typename U, typename R>
auto floor_in(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename RoundingUnits, typename U, typename R>
auto floor_in(RoundingUnits rounding_units, QuantityPoint<U, R> q);
// (No unit-only version for `Constant`: since `Constant` has no rep, we need an explicit type.)
// 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like:
// `floor_in<Type>(units, quantity)`
// a) For `Quantity` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
auto floor_in(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
auto floor_in(RoundingUnits rounding_units, QuantityPoint<U, R> q);
// c) For `Constant` inputs †
template <typename OutputRep, typename RoundingUnits, typename Unit>
constexpr auto floor_in(RoundingUnits rounding_units, Constant<Unit> c);
† Constant support is subject to compile-time arithmetic
limitations.
Returns: The largest value, which is an integer in the requested units, that is no larger than the input.
- For functions ending in
_as, this will be the same Au type category (i.e.,Quantity,QuantityPoint, orConstant) as the input. - For functions ending in
_in, this will be a raw number.
For the “explicit rep” versions, the provided type will be the rep of the return value for _as
functions, and will be the type of the return value for _in functions (since they return raw
numbers).
For “unit-only” versions, the policy for the rep is consistent with
std::floor. The output rep (_as
functions) or type (_in functions) is the same as the return type of applying std::floor to the
input rep.
Tip
If you need to floor a Quantity with integral rep, and you want to stay in the integer domain
or need constexpr compatibility, consider using int_floor_in and
int_floor_as instead.
int_round_in, int_round_as¶
Round a Quantity with integral rep to the nearest integer value in the specified units, without
leaving the integer domain.
The name int_round is shorthand for “integer-domain round”. These functions are similar to
round_in and round_as, but they do not call std::round. This provides two
advantages. First, the computation stays purely in the integer domain, rather than converting to
floating point and back. Second, these functions are constexpr compatible.
Signatures:
//
// int_round_as(): return a Quantity or QuantityPoint (depending on the input type)
//
// 1. Unit-only version (uses same Rep as input). Typical callsites look like:
// `int_round_as(units, quantity)`
// a) For `Quantity` inputs
template <typename RoundingUnits, typename U, typename R>
constexpr auto int_round_as(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename RoundingUnits, typename U, typename R>
constexpr auto int_round_as(RoundingUnits rounding_units, QuantityPoint<U, R> p);
// c) For `Constant` inputs †
template <typename RoundingUnits, typename Unit>
constexpr auto int_round_as(RoundingUnits rounding_units, Constant<Unit> c);
// 2. Explicit-Rep version (uses explicitly provided output Rep). Typical callsites look like:
// `int_round_as<Type>(units, quantity)`
// a) For `Quantity` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
constexpr auto int_round_as(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
constexpr auto int_round_as(RoundingUnits rounding_units, QuantityPoint<U, R> p);
// (No explicit-rep version for `Constant`: since the result is another `Constant`, no rep is
// needed.)
//
// int_round_in(): return a raw number
//
// 1. Unit-only version (output is Rep of input). Typical callsites look like:
// `int_round_in(units, quantity)`
// a) For `Quantity` inputs
template <typename RoundingUnits, typename U, typename R>
constexpr auto int_round_in(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename RoundingUnits, typename U, typename R>
constexpr auto int_round_in(RoundingUnits rounding_units, QuantityPoint<U, R> p);
// (No unit-only version for `Constant`: since `Constant` has no rep, we need an explicit type.)
// 2. Explicit-Rep version (output is explicitly provided type). Typical callsites look like:
// `int_round_in<Type>(units, quantity)`
// a) For `Quantity` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
constexpr auto int_round_in(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
constexpr auto int_round_in(RoundingUnits rounding_units, QuantityPoint<U, R> p);
// c) For `Constant` inputs †
template <typename OutputRep, typename RoundingUnits, typename Unit>
constexpr auto int_round_in(RoundingUnits rounding_units, Constant<Unit> c);
† Constant support is subject to compile-time arithmetic
limitations.
Returns: The nearest value, which is an integer in the requested units, to the input. When the
input is exactly halfway between two adjacent integer values, these functions round away from
zero (e.g., 0.5 rounds to 1, and -0.5 rounds to -1).
- For functions ending in
_as, this will be the same Au type category (i.e.,Quantity,QuantityPoint, orConstant) as the input. - For functions ending in
_in, this will be a raw number.
For the “explicit rep” versions, the provided type will be the rep of the return value for _as
functions, and will be the type of the return value for _in functions (since they return raw
numbers). This type must be integral (although the input rep can be any type).
For “unit-only” versions, the output rep (_as functions) or type (_in functions) is the same as
the input’s rep, which must be integral.
int_floor_in, int_floor_as¶
Round a Quantity with integral rep down to the largest integer value which is no bigger than
that quantity, without leaving the integer domain.
The name int_floor is shorthand for “integer-domain floor”. These functions are similar to
floor_in and floor_as, but they do not call std::floor. This provides two
advantages. First, the computation stays purely in the integer domain, rather than converting to
floating point and back. Second, these functions are constexpr compatible.
Signatures:
//
// int_floor_as(): return a Quantity or QuantityPoint (depending on the input type)
//
// 1. Unit-only version (uses same Rep as input). Typical callsites look like:
// `int_floor_as(units, quantity)`
// a) For `Quantity` inputs
template <typename RoundingUnits, typename U, typename R>
constexpr auto int_floor_as(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename RoundingUnits, typename U, typename R>
constexpr auto int_floor_as(RoundingUnits rounding_units, QuantityPoint<U, R> p);
// c) For `Constant` inputs †
template <typename RoundingUnits, typename Unit>
constexpr auto int_floor_as(RoundingUnits rounding_units, Constant<Unit> c);
// 2. Explicit-Rep version (uses explicitly provided output Rep). Typical callsites look like:
// `int_floor_as<Type>(units, quantity)`
// a) For `Quantity` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
constexpr auto int_floor_as(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
constexpr auto int_floor_as(RoundingUnits rounding_units, QuantityPoint<U, R> p);
// (No explicit-rep version for `Constant`: since the result is another `Constant`, no rep is needed.)
//
// int_floor_in(): return a raw number
//
// 1. Unit-only version (output is Rep of input). Typical callsites look like:
// `int_floor_in(units, quantity)`
// a) For `Quantity` inputs
template <typename RoundingUnits, typename U, typename R>
constexpr auto int_floor_in(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename RoundingUnits, typename U, typename R>
constexpr auto int_floor_in(RoundingUnits rounding_units, QuantityPoint<U, R> p);
// (No unit-only version for `Constant`: since `Constant` has no rep, we need an explicit type.)
// 2. Explicit-Rep version (output is explicitly provided type). Typical callsites look like:
// `int_floor_in<Type>(units, quantity)`
// a) For `Quantity` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
constexpr auto int_floor_in(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
constexpr auto int_floor_in(RoundingUnits rounding_units, QuantityPoint<U, R> p);
// c) For `Constant` inputs †
template <typename OutputRep, typename RoundingUnits, typename Unit>
constexpr auto int_floor_in(RoundingUnits rounding_units, Constant<Unit> c);
† Constant support is subject to compile-time arithmetic
limitations.
Returns: The largest value, which is an integer in the requested units, that is no larger than the input.
- For functions ending in
_as, this will be the same Au type category (i.e.,Quantity,QuantityPoint, orConstant) as the input. - For functions ending in
_in, this will be a raw number.
For the “explicit rep” versions, the provided type will be the rep of the return value for _as
functions, and will be the type of the return value for _in functions (since they return raw
numbers). This type must be integral (although the input rep can be any type).
For “unit-only” versions, the output rep (_as functions) or type (_in functions) is the same as
the input’s rep, which must be integral.
int_ceil_in, int_ceil_as¶
Round a Quantity with integral rep up to the smallest integer value which is at least as big as
that quantity, without leaving the integer domain.
The name int_ceil is shorthand for “integer-domain ceil”. These functions are similar to
ceil_in and ceil_as, but they do not call std::ceil. This provides two advantages.
First, the computation stays purely in the integer domain, rather than converting to floating point
and back. Second, these functions are constexpr compatible.
Signatures:
//
// int_ceil_as(): return a Quantity or QuantityPoint (depending on the input type)
//
// 1. Unit-only version (uses same Rep as input). Typical callsites look like:
// `int_ceil_as(units, quantity)`
// a) For `Quantity` inputs
template <typename RoundingUnits, typename U, typename R>
constexpr auto int_ceil_as(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename RoundingUnits, typename U, typename R>
constexpr auto int_ceil_as(RoundingUnits rounding_units, QuantityPoint<U, R> p);
// c) For `Constant` inputs †
template <typename RoundingUnits, typename Unit>
constexpr auto int_ceil_as(RoundingUnits rounding_units, Constant<Unit> c);
// 2. Explicit-Rep version (uses explicitly provided output Rep). Typical callsites look like:
// `int_ceil_as<Type>(units, quantity)`
// a) For `Quantity` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
constexpr auto int_ceil_as(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
constexpr auto int_ceil_as(RoundingUnits rounding_units, QuantityPoint<U, R> p);
// (No explicit-rep version for `Constant`: since the result is another `Constant`, no rep is
// needed.)
//
// int_ceil_in(): return a raw number
//
// 1. Unit-only version (output is Rep of input). Typical callsites look like:
// `int_ceil_in(units, quantity)`
// a) For `Quantity` inputs
template <typename RoundingUnits, typename U, typename R>
constexpr auto int_ceil_in(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename RoundingUnits, typename U, typename R>
constexpr auto int_ceil_in(RoundingUnits rounding_units, QuantityPoint<U, R> p);
// (No unit-only version for `Constant`: since `Constant` has no rep, we need an explicit type.)
// 2. Explicit-Rep version (output is explicitly provided type). Typical callsites look like:
// `int_ceil_in<Type>(units, quantity)`
// a) For `Quantity` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
constexpr auto int_ceil_in(RoundingUnits rounding_units, Quantity<U, R> q);
// b) For `QuantityPoint` inputs
template <typename OutputRep, typename RoundingUnits, typename U, typename R>
constexpr auto int_ceil_in(RoundingUnits rounding_units, QuantityPoint<U, R> p);
// c) For `Constant` inputs †
template <typename OutputRep, typename RoundingUnits, typename Unit>
constexpr auto int_ceil_in(RoundingUnits rounding_units, Constant<Unit> c);
† Constant support is subject to compile-time arithmetic
limitations.
Returns: The smallest value, which is an integer in the requested units, that is at least as large as the input.
- For functions ending in
_as, this will be the same Au type category (i.e.,Quantity,QuantityPoint, orConstant) as the input. - For functions ending in
_in, this will be a raw number.
For the “explicit rep” versions, the provided type will be the rep of the return value for _as
functions, and will be the type of the return value for _in functions (since they return raw
numbers). This type must be integral (although the input rep can be any type).
For “unit-only” versions, the output rep (_as functions) or type (_in functions) is the same as
the input’s rep, which must be integral.
Inverse functions¶
inverse_as, inverse_in¶
A unit-aware computation of 1 / x.
“Unit-aware” means that you specify the desired target unit, and the library will figure out the appropriate units for representing the 1 in 1 / x. This intelligent choice enables it to automatically handle many conversions with integer types, without the computation ever needing to leave the integer domain.
Example: inverse of 250 \,\text{Hz}
The inverse of 250 \,\text{Hz} is 0.004 \,\text{s}. If we are using integer types, of course this would truncate down to 0. However, we could choose an alternate unit — say, \text{µs} — and we would get a “nicer” answer of 4000 \,\text{µs}.
Now for Au. If you request the inverse of hertz(250) in micro(seconds), the library will
indeed return micro(seconds)(4000) — and it can perform this computation without ever
leaving the integer domain! What happens under the hood is that the value of 250 is divided
into a value of 1'000'000, not 1.
To see how we came up with this value, let’s re-express the fundamental equation. Let x be the original quantity, and y its inverse. We have:
This is a quantity equation. And since multiplication is an arbitrary-unit
operation, we can reason independently
about the unit and the value. The units on the right hand side are hertz * micro(seconds).
This is a dimensionless unit with a magnitude of 10^{-6}. The units on the left hand side
must match; therefore, we must express 1 in these units. When we do, we find its value in
these units is 10^6 — or, in C++ code, 1'000'000.
That is how the library knows to divide 250 into 1'000'000 to get an answer of 4'000 —
all without ever leaving the integer domain.
These functions include safety checks.
Quantityinputs with floating point rep are always allowed.Quantityinputs with integral rep are allowed only when the product of the input and target units — which is necessarily dimensionless — has a magnitude not greater than 10^{-6}. We chose this threshold because it means that the round-trip double inversion will be lossless for anyQuantitywhose underlying value is not greater than1'000.
As with all other library functions, you can circumvent the safety checks by using one of the
“explicit-rep” versions, which are forcing in the same way as static_cast.
Signatures:
//
// inverse_as(): return a Quantity
//
// 1. Unit-only version (including safety checks). Typical callsites look like:
// `inverse_as(units, quantity)`
template <typename TargetUnits, typename U, typename R>
constexpr auto inverse_as(TargetUnits target_units, Quantity<U, R> q);
// 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like:
// `inverse_as<Type>(units, quantity)`
template <typename TargetRep, typename TargetUnits, typename U, typename R>
constexpr auto inverse_as(TargetUnits target_units, Quantity<U, R> q);
//
// inverse_in(): return a raw number
//
// 1. Unit-only version (including safety checks). Typical callsites look like:
// `inverse_in(units, quantity)`
template <typename TargetUnits, typename U, typename R>
constexpr auto inverse_in(TargetUnits target_units, Quantity<U, R> q);
// 2. Explicit-rep version (overriding; ignores safety checks). Typical callsites look like:
// `inverse_in<Type>(units, quantity)`
template <typename TargetRep, typename TargetUnits, typename U, typename R>
constexpr auto inverse_in(TargetUnits target_units, Quantity<U, R> q);
Returns: The inverse of the input Quantity, expressed in the requested units.
Special values and language features¶
isinf¶
Indicates whether the underlying value of a Quantity is an infinity value (positive or negative).
Signature:
// 1. `Quantity` inputs
template <typename U, typename R>
constexpr bool isinf(Quantity<U, R> q);
// 2. `QuantityPoint` inputs
template <typename U, typename R>
constexpr bool isinf(QuantityPoint<U, R> q);
Returns: true if q is infinite; false otherwise.
isnan¶
Indicates whether the underlying value of a Quantity is a NaN (“not-a-number”) value.
Signature:
// 1. `Quantity` inputs
template <typename U, typename R>
constexpr bool isnan(Quantity<U, R> q);
// 2. `QuantityPoint` inputs
template <typename U, typename R>
constexpr bool isnan(QuantityPoint<U, R> q);
Returns: true if q is NaN; false otherwise.
std::numeric_limits specializations¶
Specializations for std::numeric_limits<Quantity<...>>.
For any Quantity<UnitT, Rep>, we simply delegate to std::numeric_limits<Rep> in the appropriate
way, being careful to follow the rules for specializing, and adapt the result we get. For
example, std::numeric_limits<Quantity<Hours, int>>::max() is exactly equal to
hours(std::numeric_limits<int>::max()).
Warning
Be careful about using these limits in the presence of different Units of the same Dimension. Comparison operations will compile, but may not do what you expect. Consider this example:
Clearly, we’d want this to be true… but, in converting both sides to their common type, we’d
end up multiplying the max-int on the right by 3600. What answer would we get for the
comparison? It’s far from clear.
If you use these for a single Quantity type (i.e., same Unit and Rep), they should be just
fine. (Then again—perhaps this is a good opportunity to ask yourself what you’re really
trying to accomplish, and whether using the largest finite value of a particular type is the
best way to achieve it!)
Miscellaneous¶
fmod¶
A unit-aware adaptation of std::fmod, giving the positive remainder of the division of the two
inputs.
As with the integer modulus, we first express the inputs in their common unit.
Signature:
template <typename U1, typename R1, typename U2, typename R2>
auto fmod(Quantity<U1, R1> q1, Quantity<U2, R2> q2);
Returns: The remainder of q1 / q2, in the type Quantity<U, R>, where U is the common unit
of U1 and U2, and R is the common type of R1 and R2.
hypot¶
A unit-aware adaptation of std::hypot, giving the length of the hypotenuse of a right triangle
with the given side lengths.
As with many math functions, we first express the inputs in their common unit.
Signature:
template <typename U1, typename R1, typename U2, typename R2>
auto hypot(Quantity<U1, R1> q1, Quantity<U2, R2> q2);
Returns: The hypotenuse length, in the type Quantity<U, R>, where U is the common unit of
U1 and U2, and R is the common type of R1 and R2.
remainder¶
A unit-aware adaptation of std::remainder, giving the zero-centered remainder of the division of
the two inputs.
As with the integer modulus, we first express the inputs in their common unit.
Signature:
template <typename U1, typename R1, typename U2, typename R2>
auto remainder(Quantity<U1, R1> q1, Quantity<U2, R2> q2);
Returns: The remainder of q1 / q2, in the type Quantity<U, R>, where U is the common unit
of U1 and U2, and R is the common type of R1 and R2.
-
These signatures are for purposes of illustration, not completeness. In the real code, there are additional signatures covering the case of identical inputs. We need these in order to disambiguate our
minormaximplementations with respect tostd::minandstd::max. ↩