Unit¶
A unit is a type which represents a unit of measure. Examples include Meters
, Radians
,
Hours
, and so on.
Users can work with units as either types or instances, and can freely convert between these representations. That is to say: units are monovalue types.
Identifying unit types¶
A unit is not forced to be a specialization of some central type template, such as a hypothetical
Unit<...>
. Rather, it’s more open ended: a unit can be any type which fulfills certain
defining properties.
To be a unit, a type U
:

Must contain a public type alias,
U::Dim
, which refers to a valid dimension type. 
Must contain a public type alias,
U::Mag
, which refers to a valid magnitude type. 
Must be a monovalue type.

May contain a
static constexpr
member namedlabel
, which is a Cstyleconst char[]
(not aconst char*
).^{1} 
May contain a
static constexpr
member functionorigin()
, which returns a quantity whose dimension type isU::Dim
.
A custom origin()
is very rarely needed. Both labels and origins will be
discussed further below.
Making unit types¶
Although every unit type needs Dim
and Mag
members, users won’t need to add them directly.
Rather, the best way to make a unit type is by combining existing unit types via supported
operations. This approach has two key advantages relative to defining your unit as
a fully manual struct
.

If your input types are all valid units, your output type will be too.

It makes the definition more readable and physically meaningful.
To give your unit type the best ergonomics, follow our howto guide for defining new units.
Unit labels¶
Every unit has a label. Its label is a constexpr const char[]
of the appropriate size.
For a unit type U
, or instance u
, we can access the label as follows:
unit_label<U>()
unit_label(u)
Note that the u
in unit_label(u)
is a unit slot, so you
can pass anything that “acts like a unit” to it. For instance, you can say unit_label(meters)
;
you don’t need to write unit_label(Meters{})
.
This function returns a reference to the array, which again is a compile time constant.
Note especially that the type is an array ([]
). A pointer (*
) is not acceptable. This is
so that we can support sizeof(unit_label(u))
.
Using Cstyle char
arrays for our labels makes Au more friendly for embedded users, because it
gives them full access to the labels without forcing them to depend on <string>
or <iostream>
.
[UNLABELED_UNIT]
¶
If a unit does not have an explicit label, we will try to generate one automatically. If we’re
unable to do so, we fall back to the “default label”, which is "[UNLABELED_UNIT]"
.
This is a label just like any other: we do not attempt to “propagate the unlabeledness”. As
a concrete example, if Foos
is an unlabeled unit, then the label for Nano<Foos>{} / Seconds{}
would be "n[UNLABELED_UNIT] / s"
. This is to preserve as much structure as possible for end
users, so they have the best chance of recognizing the offending unit, and perhaps upgrading it.
Note
A key design goal is for every combination of meaningfully labeled units, by every supported operation, to produce a meaningfully labeled unit. Right now, the only missing operation is scaling a unit by a magnitude. We are tracking this in #85.
Unit symbols¶
Unit symbols provide a way to create Quantity
instances concisely: by simply multiplying or
dividing a raw number by the symbol.
For example, suppose we create symbols for Meters
and Seconds
:
Then we can write 3.5f * m / s
instead of (meters / second)(3.5f)
.
Creation¶
There are two ways to create an instance of a unit symbol.

Call
symbol_for(your_units)
. PRO: The argument acts as a unit slot, giving maximum flexibility and composability.
 CON: Instantiating the
symbol_for
overload adds to compilation time (although only very slightly).

Make an instance of
SymbolFor<YourUnits>
. PRO: This directly uses the type itself without instantiating anything else, so it should be the fastest to compile.
 CON: Since the argument is a type, it’s less flexible and more awkward to compose.
Examples of both methods
These are easier to compose, although at the cost of instantiating an extra function.
Prefixed symbols¶
To create a symbol for a prefixed unit, both of the ways mentioned above (namely, calling
symbol_for()
, and creating a SymbolFor<>
instance) will still work. However, there is also
a third way: you can use the appropriate prefix applier with an
existing symbol for the unit to be prefixed. This can be concise and readable.
Example: creating a symbol for Nano<Meters>
Assume we have a unit Meters
, which has a quantity maker meters
and a symbol m
. Here are
your three options for creating a symbol for the prefixed unit Nano<Meters>
.
Operations¶
Each operation with a SymbolFor
consists in multiplying or dividing with some other family of
types.
Raw numeric type T
¶
Multiplying or dividing SymbolFor<Unit>
with a raw numeric type T
produces a Quantity
whose rep
is T
, and whose unit is derived from Unit
.
In the following table, we will use x
to represent the value that was stored in the input of type
T
.
Operation  Resulting Type  Underlying Value  Notes 

SymbolFor<Unit> * T 
Quantity<Unit, T> 
x 

SymbolFor<Unit> / T 
Quantity<Unit, T> 
T{1} / x 
Disallowed for integral T 
T * SymbolFor<Unit> 
Quantity<Unit, T> 
x 

T / SymbolFor<Unit> 
Quantity<UnitInverseT<Unit>, T> 
x 
Quantity<U, R>
¶
Multiplying or dividing SymbolFor<Unit>
with a Quantity<U, R>
produces a new Quantity
. It has
the same underlying value and same rep R
, but its units U
are scaled appropriately by Unit
.
In the following table, we will use x
to represent the underlying value of the input quantity —
that is, if the input quantity was q
, then x
is q.in(U{})
.
Operation  Resulting Type  Underlying Value  Notes 

SymbolFor<Unit> * Quantity<U, R> 
Quantity<UnitProductT<Unit, U>, R> 
x 

SymbolFor<Unit> / Quantity<U, R> 
Quantity<UnitQuotientT<Unit, U>, R> 
R{1} / x 
Disallowed for integral R 
Quantity<U, R> * SymbolFor<Unit> 
Quantity<UnitProductT<U, Unit>, R> 
x 

Quantity<U, R> / SymbolFor<Unit> 
Quantity<UnitQuotientT<U, Unit>, R> 
x 
SymbolFor<OtherUnit>
¶
Symbols compose: the product or quotient of two SymbolFor
instances is a new SymbolFor
instance.
Operation  Resulting Type 

SymbolFor<Unit> * SymbolFor<OtherUnit> 
SymbolFor<UnitProductT<Unit, OtherUnit>> 
SymbolFor<Unit> / SymbolFor<OtherUnit> 
SymbolFor<UnitQuotientT<Unit, OtherUnit>> 
Unit origins¶
The “origin” of a unit is only useful for QuantityPoint
, our affine space
type. Even then, the origin by itself is not
meaningful. Only the difference between the origins of two units is meaningful.
You would use this to implement an “offset” unit, such as Celsius
or Fahrenheit
. However, note
that both of these are already implemented in the library.
The origin defaults to ZERO
if not supplied.
Types for combined units¶
A core tenet of Au’s design philosophy is to avoid giving any units special status. Every named unit enters into a unit computation on equal footing. We will keep track of the accumulated powers of each named unit, cancelling as appropriate. The final form will follow these rules.

Every power of a named unit will be represented according to the representation table. That is, it will be omitted if its power is zero, and will otherwise appear as one of
Pow
,RatioPow
, or the bare unit itself. 
If only one named unit remains with nonzero power, then that named unit power (as represented in the previous rule) is the complete type.

If multiple named units remain with nonzero power, then their representations (according to rule 1) are combined as the elements of a variadic
UnitProduct<...>
pack.
Warning
The ordering of the bases is deterministic, but is implementation defined, and can change at any time. It is a programming error to write code that assumes any specific ordering of the units in a pack.
A few examples
We have omitted the au::
namespace in the following examples for greater clarity.
Unit expression  Resulting unit type 

squared(Meters{}) 
Pow<Meters, 2> 
Meters{} / Seconds{} 
UnitProduct<Meters, Pow<Seconds, 1>> 
Seconds{} * Meters{} / Seconds{} 
Meters 
Operations¶
These are the operations which each unit type supports. Because a unit must be a monovalue type, it can take the form of either a type or an instance. In what follows, we’ll use this convention:
 Capital identifiers (
U
,U1
,U2
, …) refer to types.  Lowercase identifiers (
u
,u1
,u2
, …) refer to instances.
Multiplication¶
Result: The product of two units.
Syntax:
 For types
U1
andU2
:UnitProductT<U1, U2>
 For instances
u1
andu2
:u1 * u2
Division¶
Result: The quotient of two units.
Syntax:
 For types
U1
andU2
:UnitQuotientT<U1, U2>
 For instances
u1
andu2
:u1 / u2
Powers¶
Result: A unit raised to an integral power.
Syntax:
 For a type
U
, and an integral powerN
:UnitPowerT<U, N>
 For an instance
u
, and an integral powerN
:pow<N>(u)
Roots¶
Result: An integral root of a unit.
Syntax:
 For a type
U
, and an integral rootN
:UnitPowerT<U, 1, N>
(because the N^\text{th} root is equivalent to the \left(\frac{1}{N}\right)^\text{th} power)
 For an instance
u
, and an integral rootN
:root<N>(u)
Helpers for powers and roots¶
Units support all of the power helpers. So, for example, for a unit instance
u
, you can write sqrt(u)
as a more readable alternative to root<2>(u)
.
Scaling by Magnitude
¶
Result: A new unit which has been scaled by the given magnitude. More specifically, for a unit
instance u
and magnitude instance m
, this operation:
 Preserves the dimension of
u
.  Scales the magnitude of
u
by a factor ofm
.  Deletes the label of
u
.  Preserves the origin of
u
.
Syntax:
u * m
Traits¶
Because units are monovalue types, each trait has two forms: one for types, and another for instances.
Additionally, the parameters in the instance forms will usually act as unit
slots. This means you can, for example, write
unit_ratio(feet, meters)
, which can be convenient.
Warning
The only unit trait whose parameters are not unit slots is is_unit(u)
. This is because of
its name. It will return true
only if you pass a unit type: passing the unit instance
Meters{}
returns true
, but passing the quantity maker meters
returns false
.
If you want to check whether your instance is compatible with a unit
slot, use fits_in_unit_slot(u)
.
Sections describing bool
traits will be indicated with a trailing question mark, "?"
.
Is unit?¶
Result: Indicates whether the argument is a valid unit.
Warning
We don’t currently have a trait that can detect whether or not a type is a monovalue type. Thus, the current implementation only checks whether the dimension and magnitude are valid. Until we get such a trait, authors of unit types are responsible for satisfying the monovalue type requirement.
Syntax:
 For type
U
:IsUnit<U>::value
 For instance
u
:is_unit(u)
Warning
This will only return true if u
is an instance of a unit type, such as Meters{}
. It will
return false
for a quantity maker such as meters
.
This is what you want if you are trying to figure out whether the type of your instance would be
suitable as the first template parameter for Quantity
or QuantityPoint
.
If you are trying to figure out whether your instance u
is suitable for a unit
slot, call fits_in_unit_slot(u)
instead.
Fits in unit slot?¶
Result: Indicates whether the argument can be validly passed to a unit slot in an API.
This trait is instanceonly: there is no reason to apply this to types, so we do not provide a typebased API.
Syntax:
 For instance
u
:fits_in_unit_slot(u)
Warning
This can return true even if u
is not an instance of a unit type. For example,
fits_in_unit_slot(meters)
returns true, even though the type of meters
is
QuantityMaker<Meters>
, and thus, not a unit.
If you want to stringently check whether u
is a unit — say, to determine whether its type
is suitable as the first template parameter of Quantity
— then call is_unit(u)
instead.
Has same dimension?¶
Result: Indicates whether two units have the same dimension.
Syntax:
 For types
U1
andU2
:HasSameDimension<U1, U2>::value
 For instances
u1
andu2
:has_same_dimension(u1, u2)
Are units quantityequivalent?¶
Result: Indicates whether two units are quantityequivalent. This means that they have the same dimension and same magnitude. Quantities of quantityequivalent units may be trivially converted to each other with no conversion factor.
For example, Meters{} * Hertz{}
is not the same unit as Meters{} / Seconds{}
, but they are
quantityequivalent.
Syntax:
 For types
U1
andU2
:AreUnitsQuantityEquivalent<U1, U2>::value
 For instances
u1
andu2
:are_units_quantity_equivalent(u1, u2)
Are units pointequivalent?¶
Result: Indicates whether two units are pointequivalent. This means that they have the same
dimension, same magnitude, and same origin. QuantityPoint
instances of pointequivalent units
may be trivially converted to each other with no conversion factor and no additive offset.
For example, while Celsius
and Kelvins
are quantityequivalent, they are not
pointequivalent.
Syntax:
 For types
U1
andU2
:AreUnitsPointEquivalent<U1, U2>::value
 For instances
u1
andu2
:are_units_point_equivalent(u1, u2)
Is dimensionless?¶
Result: Indicates whether the argument is a dimensionless unit.
Syntax:
 For type
U
:IsDimensionless<U>::value
 For instance
u
:is_dimensionless(u)
Is unitless unit?¶
Result: Indicates whether the argument is a “unitless unit”: that is, a dimensionless unit whose magnitude is 1.
Syntax:
 For type
U
:IsUnitlessUnit<U>::value
 For instance
u
:is_unitless_unit(u)
Unit ratio¶
Result: The magnitude representing the ratio of the input units’ magnitudes.
For units with nontrivial dimension, there is no such thing as “the” magnitude of a unit: it is not physically meaningful or observable. However, the ratio of units’ magnitudes is well defined, and that is what this trait produces.
For example, the unit ratio of Feet
and Inches
is mag<12>()
, because a foot is 12 times as big
as an inch.
Syntax:
 For types
U1
andU2
:UnitRatioT<U1, U2>::value
 For instances
u1
andu2
:unit_ratio(u1, u2)
Origin displacement¶
Result: The displacement from the first unit’s origin to the second unit’s origin.
Recall that there is no such thing as “the” origin of a unit: it is not physically meaningful or observable. However, the displacement from one unit’s origin to another is well defined, and that is what this trait produces.
For example, the origin displacement from Kelvins
to Celsius
is equivalent to
273.15 \,\text{K}.
Syntax:
 For types
U1
andU2
:OriginDisplacement<U1, U2>::value()
 For instances
u1
andu2
:origin_displacement(u1, u2)
Associated unit¶
Result: The actual unit associated with a unit slot that
is associated with a Quantity
type. Here are a few examples.
round_in(meters, feet(20));
// ^^^^^^
round_in(Meters{}, feet(20));
// ^^^^^^^^
using symbols::m;
round_in(m, feet(20));
// ^
feet(6).in(inches);
// ^^^^^^
feet(6).in(Inches{});
// ^^^^^^^^
The underlined arguments are all unit slots. The kinds of things that can be passed here include
a QuantityMaker
for a unit, a constant, a unit symbol, or simply
a unit type itself.
The use case for this trait is to implement the unit slot argument for a function.
Syntax:
 For a type
U
:AssociatedUnitT<U>
 For an instance
u
:associated_unit(u)
Associated unit (for points)¶
Result: The actual unit associated with a unit slot that is associated with a quantity point type. Here are a few examples.
round_in(meters_pt, milli(meters_pt)(1200));
// ^^^^^^^^^
round_in(Meters{}, milli(meters_pt)(1200));
// ^^^^^^^^
meters_pt(6).in(centi(meters_pt));
// ^^^^^^^^^^^^^^^^
meters_pt(6).in(Centi<Meters>{});
// ^^^^^^^^^^^^^^^
The underlined arguments are unit slots for quantity points. In practice, this will be either
a QuantityPointMaker
for some unit, or a unit itself.
The use case for this trait is to implement a function or API that takes a unit slot, and is associated with quantity points.
Syntax:
 For a type
U
:AssociatedUnitForPointsT<U>
 For an instance
u
:associated_unit_for_points(u)
Common unit¶
Result: The largest unit that evenly divides its input units. (Read more about the concept of common units.)
A specialization will only exist if all input types are units.
If the inputs are units, but their Dimensions aren’t all identical, then the request is illformed and we will produce a hard error.
It may happen that the input units have the same Dimension, but there is no unit which evenly divides them (because some pair of input units has an irrational quotient). In this case, there is no uniquely defined answer, but the program should still produce some answer. We guarantee that the result is associative, and symmetric under any reordering of the input units. The specific implementation choice will be driven by convenience and simplicity.
Syntax:
 For types
Us...
:CommonUnitT<Us...>
Common point unit¶
Result: The largestmagnitude, highestorigin unit which is “common” to the units of
a collection of QuantityPoint
instances. (Read more about the concept of
common units for QuantityPoint
.)
The key goal to keep in mind is that for a QuantityPoint
of any unit U
in Us...
, converting
its value to the common pointunit should involve only:
 multiplication by a positive integer
 addition of a nonnegative integer
This helps us support the widest range of Rep types (in particular, unsigned integers).
As with CommonUnitT
, this isn’t always possible: in particular, we can’t do this for units with
irrational relative magnitudes or origin displacements. However, we still provide some answer,
which is consistent with the above policy whenever it’s achievable, and produces reasonable results
in all other cases.
A specialization will only exist if the inputs are all units, and will exist but produce a hard error if any two input units have different Dimensions. We also strive to keep the result associative, and symmetric under interchange of any inputs.
Syntax:
 For types
Us...
:CommonPointUnitT<Us...>

Unit types defined by the library may also use
au::detail::StringConstant<N>
for some integer lengthN
. Since this is in thedetail
namespace, we wanted to deemphasize it in this document. ↩