Quantify
An extensible C++ units library
Loading...
Searching...
No Matches
Getting Started

Introduction

Quantify is C++ library that deals with engineering units. It can deduce units across mathematical expressions and reduce those units to their simplest representation. That is, if a speed [m/s] is multiplied by time [s], the resulting unit will be a distance [m], and not [s*m/s]. Units are checked for consistency at compile time and unit conversions are performed when needed.

Quantify allows for code to become fully unit-agnostic. Functions can specify which unit scales their parameters belong to (ie: mass, distance, time,...) thus allowing for any unit in those scales to be used. Here is a short example of an implementation of Newton's second law as a unit-agnostic function:

auto second_law(
mass::quantity auto m,
Quantity<frac<distance::scale, mul<time::scale, time::scale>>> auto a
) {
return m * a; // Force = Mass * Acceleration
}

Nowhere on this function is any unit specified, but the output will always be convertible to Newtons. The inputs m and a can be of any unit as long as they represent mass and acceleration respectively. This is a powerful feature that enables writing unit-consistent code without actually caring about which units will eventually be used.

For more information, see Writing Unit-Agnostic Functions

Key Concepts

The library is developed around three main concepts: Quantities, Units, and Unit Scales.

Units represent your typical engineering units such as Meters or Seconds, they can be combined using unit expressions to represent more complex units such as Newtons [kg*m/s^2].

Unit Scales group units that can be converted using a rational conversion factor. For example, the distance scale contains all the units that represent length quantities such as Meters or Miles. In some cases, such as for temperature scales, units can be converted between different unit scales. See Custom Conversions for a detailed explanation as to how or why this may be needed.

Lastly, Quantities are objects of the wrapper class quantify::quantity which annotates a value with a unit. The unit is only relevant at compile time so there is no runtime memory overhead. This is the class that implements unit deduction logic.

See also
Quantities
Unit Expressions
Unit Scales

Built-in and Custom Units

Quantify defines all base and derived units in the International System of Units. On top of that, Quantify also defines other non SI units such as those found in the Imperial System. More units may be added over time as the library evolves. Finally, Quantify allows for new units to be defined in existing scales. For instance, a new distance unit could be added like this:

UNIT_IN_SCALE(quantify::distance::scale, lunar_distance, "LD", 384400000, 1);
#define UNIT_IN_SCALE(SCALE, NAME, SYMBOL, FACTOR_NUM, FACTOR_DEN)
Definition unit_macros.h:50

Where the unit Lunar Distance [LD] is specified with a conversion factor of 384,400,000/1 which corresponds to 1 Lunar Distance being 384,400,000 meters given that meters are the base unit for the distance scale. Similarly, more scales can also be added. See Custom Scales and Units for more information.

See also
Base Units
Derived Units
Custom Scales and Units

Unit and Scale Conversions

Units belonging to the same scale can always be converted among each other since they are related by a rational factor. For example, Kilometers are in the same scale as Miles (the distance scale) and thus a quantity represented in either unit can always be converted to the other by multiplying or dividing by a factor. In this case, the factor would be computed at compile time from the individual defining factors for each unit:

$$([km]\rightarrow[m]): 1000[m]/1[km]$$ $$([mi]\rightarrow[m]): 1609[m]/1[mi]$$

Then,

$$([km]\rightarrow[mi]): ([km]\rightarrow[m])*([m]\rightarrow[mi])$$ $$([km]\rightarrow[mi]): \frac{([km]\rightarrow[m])}{([mi]\rightarrow[m])} = \frac{1000[m]/1[km]}{1609[m]/1[mi]} = \frac{1000[mi]}{1609[km]} \approx 0.6215 [mi/km]$$

So Kilometers can be converted to Miles by multiplying times 0.6215. Similarly, Miles can be converted to Kilometers by dividing by the same factor. In Quantify terms, this is only possible because both units belong to the same scale and thus their defining factors are in terms of the same base unit, Meters, so they can be combined.

By default, units from different scales cannot be converted between one another. That is, their values are not related by a conversion factor. For instance, 1 meter cannot be converted to an equivalent amount of seconds.

There are cases, however, where it may make sense to establish a custom conversion between two different scales. Quantify can handle these cases by allowing custom scale conversions to be defined with the SCALE_CONVERSION macro.

SCALE_CONVERSION(FromScale, ToScale)
#define SCALE_CONVERSION(FROM, TO)
Definition unit_macros.h:72
#define SCALE_FORWARD_CONVERSION(...)
Definition unit_macros.h:78
#define SCALE_BACKWARD_CONVERSION(...)
Definition unit_macros.h:94

One specific example is that of the temperature scales. The units (Celsius, Kelvin and Fahrenheit) all represent a quantity of temperature, but converting a value from one to another is not a simple question of multiplying by factor. An offset needs to be added or subtracted in each conversion.

SCALE_CONVERSION(scales::kelvin::scale, scales::celsius::scale)
SCALE_CONVERSION(scales::celsius::scale, scales::fahrenheit::scale)
SCALE_FORWARD_CONVERSION((it * 9 / 5) + 32)
SCALE_BACKWARD_CONVERSION((it - 32) * 5 / 9)
SCALE_CONVERSION(scales::kelvin::scale, scales::fahrenheit::scale)
SCALE_FORWARD_CONVERSION(((it - 273.15) * 9 / 5) + 32)
SCALE_BACKWARD_CONVERSION(((it - 32) * 5 / 9) + 273.15)

Scale conversions are rarely performed automatically as they are inherently non-linear, the main way to invoke them is by forcing unit conversions. Mathematical expressions usually cannot be made agnostic to non-linear conversions. For instance, formulas involving Celsius don't work the same way if Kelvin is used instead. On the other hand, Newton's second law of motion (F=m*a) works the same with Kilograms, Pounds, or Metric Tons. The unit for the output value will change, but the mathematical equality will hold.

See also
Forcing Units and Performing Conversions

Installation

For the simplest case, using CMake, add this to the project's CMakeLists.txt:

include(FetchContent)
FetchContent_Declare(quantify
GIT_REPOSITORY https://github.com/castle055/quantify.git
GIT_TAG <git-tag> # any version tag, ie: 'main', 'dev', 'v1.0.0', ... or a commit hash
FIND_PACKAGE_ARGS
)
FetchContent_MakeAvailable(quantify)

Then link the project's target against quantify:

target_link_libraries(MyExecutable PRIVATE quantify)
See also
Installation & Build Guide