Home

Awesome

-<img align="right" src="SI-logo.jpg">

Continuous Integration Codacy Badge API Documentation CodeQL

GitHub Releases GitHub license

SI - Type safety for physical units

A header only c++ library that provides type safety and user defined literals for handling physical values defined in the International System of Units regulated by the International Bureau of Weights and Measures (BIPM) and published in the SI brochure. For a quick start see the installation guide. Contributions and comments are welcome, please check the contribution guidelines for further information.

An illustrative example:

#include <SI/electric_charge.h>
#include <SI/mass.h>

using namespace SI::literals;

constexpr auto one_kilogram = 1.0_kg;
constexpr auto ten_coulomb = 5.0_A * 2.0_s;
constexpr auto half_an_ampere = ten_coulomb / 20.0_s;
constexpr auto thousand_grams = one_kilogram.as<SI::gram_t>();

void calculate_mass(const SI::kilo_gram_t<long double> &kg) {
  // do something meaningful here
}

int main(int, char **) {
  calculate_mass(one_kilogram);
  return 0;
}

SI provides conversions and arithmetic operations with values of any of the International System of Units with strong type safety at compile time. All units are special typedefs of the template struct SI::unit_t. Only the value of the unit is stored internally, the ratio (i.e. milli, micro, kilo...) is determined as a type trait to allow all units to have the same resolution across the whole implemented ratios. SI handles operations of units of the same ratios as well as when the ratios are different. See the documentation implementation details for further information. Operations between units of the same ratio are overhead-free, else there is an additional computation cost to adjust the values to the units. Passing the flag SI_DISABLE_IMPLICIT_RATIO_CONVERSION to the compiler implicit ratio conversion is not done and fails with a compiler error. See the continuous benchmark results for a comparison between the reference measurements and the implementation in SI.

It is possible to supply custom ratios to the built-in types and they are fully compatible for calculation with other units. However, the necessary literals or typedefs have to be supplied by the user. For instance SI::velocity_t<double, std::ratio<1000, 36>> would be "kilometre per one-hundredth-of-an-hour".

Converting between units is either done with the as<unit_t>() member function of unit_ or the free function SI::unit_cast<unit_t>(unit_t u). This will convert a value of the same type but a different ratio.

SI Base units

For each unit the available literals are the implemented ratios prefixed with an underscore. i.e. _mm. _km. Generally the ratios follow metric prefixes of the international system of units The typedefs are prefixed (or in rare cases interfixed) with the standard metric prefixes. i.e. metre_t, milli_metre_t, kilo_metre_t. The prefix or interfix is marked with an * in the tables below. Units that have defined typedefs and literals can be converted to strings using stream operators or the SI::to_string function. To use the stream operators, include the header file SI/stream.h

UnitDimension SymbolUnit Symbolimplemented ratiosunit typedefs
LengthLm10<sup>-18</sup> to 10<sup>18</sup>*_metre_t
TimeTs10<sup>-18</sup> to 10<sup>0</sup> and 60/1, 3600/1*_seconds_t, minutes_t, hours_t
Mass*Mkg10<sup>-15</sup> to 10<sup>18</sup>*_gram_t, *_ton_t
Electric currentIA10<sup>-18</sup> to 10<sup>18</sup>*_ampere_t
Thermodynamic temperature**tK10<sup>-18</sup> to 10<sup>18</sup>*_kelvin_t
Amount of substanceNmol10<sup>-18</sup> to 10<sup>18</sup>*_mol_t
LuminosityJcd10<sup>-18</sup> to 10<sup>18</sup>*_candela_t

* for mass the base ratio is kg (not g) as defined in the SI unit table. So there is a mismatch between the literal prefix and the internal representation.

** The dimension symbol for thermodynamic temperature should be Θ (Theta), but the current implementation does not allow for non-ASCII symbols or multi-char symbols

Special Units

UnitDimension SymbolExponentUnit Symbolimplemented ratiosunit typedefs
AreaL2m210<sup>-18</sup> to 10<sup>18</sup>square_*_metre_t
VolumeL3m310<sup>-18</sup> to 10<sup>18</sup>cubic_*_metre_t
FrequencyT-1Hz10<sup>-18</sup> to 10<sup>18</sup>*_hertz_t
Angle*r1rad10<sup>-18</sup> to 1*_radian_t
Angle (Degrees)*r1degmicro, milli, 1*_radian_t
Solid Angle*R1sr10<sup>-18</sup> to 1*_steradian_t

* Angle, Angle (degree) and solid angle are simple containers, not containing any functionality to do angle/solid-angle computation such as an overflow after 2*pi. Converting between radians and degree might lose precision, especially if working with long doubles because the ratios not precise enough, as they have to be represented as long ints

Derived units with special names

All units that can be built from other units decay to the respective units by inverting the mathematical operation. I.e if Q = I * T then Q / I = T and Q / T = I

UnitDimension SymbolUnit Symbolbuildable fromimplemented literalsunit typedefs
Velocityvm/sL / Tm_p_s, km_p_h, _cmetre_per_second_t, kilometre_per_second_t, speed_of_light_t
Accelerationam/s^2v / T, v^2 / Lnonenone
Electric chargeQCI * TaC to EC*_coulomb_t
Electric potentialUVP / I, E/QaV to EV*_volt_t
Electric resistanceO*Ohm (Ω)U / I, 1/GaOhm to EOhm*_ohm_t
Electric conductanceGSI / U, 1/RaS to ES*_siemens_t
Electric capacityCFQ / UaF to EF*_farad_t
ForceFNM * aaN to EN*_newton_t
PressureppaF / L^2apa to Epa*_pascal_t
EnergyEJF * L, p * L^3aJ to EJ*_joule_t
PowerPWE/TaW to EW*_watt_t
Magnetic Fluxf*WbU * TaWb to EWb*_weber_t
Magnetic FieldBTf/L^2aT to ET*_tesla_t
Momentumo*kg⋅m/sM * vnonenone
InductancelHf / IaH to EH*_henry_t
Luminous fluxm*lmJ * Ralm to Elm*_lumen_t
Luminancei*lxm / aalx to Elx*_lux_t
RadioactivityABqaBq to EBq*_becquerel_t
Absorbed DoseDGyaGy to EGy*_gray_t
Equivalent DoseHSvaSv to ESv*_sievert_t
Catalytic activityKkatN / Takat to Ekat*_katal_t
Surface flowsm^2/sL^2 / Tnone
Volumetric flowVm^3/sL^3 / Tnone
Angular frequencyw*rad/sr / Tnone

* These dimensions do not yet have the correct symbols, because the current implementation does not allow for non-ASCII symbols or multi-char symbols. The dimension symbol for electric resistance should be Ω (Ohm) and for magnetic flux Φ (Phi). Luminance should be E<sub>b</sub>. Angular velocity should be ω (omega). Luminous flux should be Φ<sub>v</sub> which is even less supported than Φ (Phi) itself.

Non-Standard Units

Non standard units are not regulated by the BIPM but are accepted for use with the SI standard.

UnitDimension Symbolliteralsimplemented ratiosunit typedefs
Astronomic Units of lengthLAU, ly, pc149597870691:1 (AU), 9460730777119564:1 (ly), 30856775814913700:1 (pc)astronomic_unit_t (_AU), lightyear_t (_ly), parsec_t (_pc)

Building & compatibility

SI is a header-only library that uses C++17 features. Building is tested using cmake > 3.23 and verified for g++7, g++8, clang5, clang6, clang7, msvc 19, and AppleClang 10.0. I recommend using conan 2.0 to download any dependencies for testing, but can be used without it, if the tests are not built.

SI uses CMakeDeps generator of conan to find dependencies. To install the dependencies use

conan install . --output-folder=build --build=missing --settings=build_type=Debug

substitute --settings=build_type=Debug with --settings=build_type=Release to switch between debug and release builds.

By using the CMAkeDeps generator, you can either build manually or use the CMake presets provided.

cmake --preset=ci-ninja-debug
cmake --build build

Installing

The default installation location for SI is /usr/local/lib/SI. SI can be installed using raw cmake, cpack (cmakes package mechanism), or as a conan.io package provided from conan center

See the installation guide for detailed instructions

Including with FetchContent

Note: Getting SI as a conan package is preferred.

To install with CMake's FetchContent, add the following to your CMakeLists.txt

include(FetchContent)

FetchContent_Declare(
  SI
  GIT_REPOSITORY https://gitlab.com/bernedom/SI.git
  # This will get the latest version
  # To pin to a specific version or hash, add the version/hash here instead
  # (e.g. 2.5.1 or 63b267211a6f256f7ba8d5a26e17138bbcf95ba8)
  GIT_TAG main
)

FetchContent_MakeAvailable(SI)

# ...

# Link the library to your target. Change this as needed!
target_link_libraries(${PROJECT_NAME} PUBLIC SI::SI)

Packaging

SI is available as 'raw' download from this repository but also as conan package. Getting SI from conan-center is the preferred way.

Implementation details

For further information and deeper technical details refer to the implementation details document

Breaking changes between versions 1.x.x and versions 2.x.x

Versions above 2.0.0 will not be fully compatible with older versions because of the breaking changes mentioned in the changelog. From version 2.0.0 on the conan package is named lower case si to conform to the conan naming convention.