This is a header-only library that implements compile time dimensional and orientational analysis in C++ through template meta-programming. The library defines template classes matching C++'s arithmetic built-in types whose template argument is the dimensions. It also provides a generic container for adding dimensions to other types.
The library is composed only of header files. To use it, simply include the file 'dimensional_analysis.h'. The compilers tested were
- MS Visual C++ 19.00.24215.1;
- GCC - 7.2.0;
- NVCC - 8.0.60.
However, the library should work with any C++11 compliant compiler.
This library supports a number of compilation options through macro definitions:
-
#define SKIP_DIMENSIONAL_ANALYSIS- if defined the dimensional analysis is skipped entirely removing any of the library's overhead and speeding the up the compilation; -
#define <LIB-TYPE-NAME> <lib-type-name>- these directives allow changing the names of the library's primitive types:#define INT8 <name>- defaults toint8and encapsulates the built-in typeINT8_T;#define INT16 <name>- defaults toint16and encapsulates the built-in typeINT16_T;#define INT32 <name>- defaults toint32and encapsulates the built-in typeINT32_T;#define INT64 <name>- defaults toint64and encapsulates the built-in typeINT64_T;#define UINT8 <name>- defaults touint8and encapsulates the built-in typeUINT8_T;#define UINT16 <name>- defaults touint16and encapsulates the built-in typeUINT16_T;#define UINT32 <name>- defaults touint32and encapsulates the built-in typeUINT32_T;#define UINT64 <name>- defaults touint64and encapsulates the built-in typeUINT64_T;#define FLOAT32 <name>- defaults tofloat32and encapsulates the built-in typeFLOAT32_T;#define FLOAT64 <name>- defaults tofloat64and encapsulates the built-in typeFLOAT64_T;#define FLOAT128 <name>- defaults tofloat128and encapsulates the built-in typeFLOAT128_T;
-
#define <ACTUAL-TYPE-NAME> <actual-type-name>#define INT8_T <name>- defaults tostd::int8_torsigned charin CUDA;#define INT16_T <name>- defaults tostd::int16_torsigned shortin CUDA;#define INT32_T <name>- defaults tostd::int32_torsigned intin CUDA;#define INT64_T <name>- defaults tostd::int64_torsigned long longin CUDA;#define UINT8_T <name>- defaults tostd::uint8_torunsigned charin CUDA;#define UINT16_T <name>- defaults tostd::uint16_torunsigned shortin CUDA;#define UINT32_T <name>- defaults tostd::uint32_torunsigned intin CUDA;#define UINT64_T <name>- defaults tostd::uint64_torunsigned long longin CUDA;#define FLOAT32_T <name>- defaults tofloat;#define FLOAT64_T <name>- defaults todouble;#define FLOAT128_T <name>- defaults tolong double.
The primitive types in the library are template classes that can be used to replace C++'s arithmetic built-in types. The template argument serves as the dimension.
int32<Adimensional> b(42); // 'a' is adimensional
uint64<> b(42); // 'b' is also adimensional
float32<Time> c(3.14); // 'c' has time dimensions
All the operations available for the built-in types are also available for the library's types. Compilation will fail with an undefined struct, that acts as an error message, whenever the operations do not have the correct units
float32<Time> a = float32<Time>(3.14) + float32<Time>(2.7); // compiles
float32<Time> b = float32<Length>(3.14) + float32<Time>(2.7); // does not compile
float64<Acceleration> ac = (float64<Velocity>(3e8) - float64<Length>(3.14) / float32<Time>(2.7)) / float64<Time>(1.0); // compiles
The C++ arithmetic built-in types can be operated with the library's types and are interpreted as Adimensional. The operators +, -, *, /, and % are implemented between types with any dimensions. The bitwise operators ~, |, ^, &, <<, and >> are only defined for adimensional quantities.
The library defines literals for declaring dimensioned quantities. The names coincide with the names given to the dimensions (listed below). Before the name of the dimension a suffix can be added to define the arithmetic type:
_s8_ ## <dim-name>- declares aINT8_T;_s16_ ## <dim-name>- declares aINT16_T;_s32_ ## <dim-name>or just_ ## <dim-name>- declares aINT32_T;_s64_ ## <dim-name>- declares aINT64_T;_u8_ ## <dim-name>- declares aUINT8_T;_u16_ ## <dim-name>- declares aUINT16_T;_u32_ ## <dim-name>- declares aUINT32_T;_u64_ ## <dim-name>- declares aUINT64_T;_f32_ ## <dim-name>- declares aFLOAT32_T;_f64_ ## <dim-name>or just_ ## <dim-name>- declares aFLOAT64_T;_f128_ ## <dim-name>- declares aFLOAT128_T.
The missing <dim-name> can be from the following list of dimensions:
Acceleration;Area;Capacitance;Charge;Current;ElectricDisplacementField;ElectricField;Energy;Force;Frequency;Inductance;Length;LengthX;LengthY;LengthZ;Mass;Permeability;Permittivity;SpatialFrequency;Temperature;Time;Velocity;Volume.
For example:
float64<Length>(8.0) + 7.0_f64_Length; // compiles
float64<Time>(8.0) + 7.0_Length; // doesn't compiles (the literal is the same as the line above)
Most mathematical functions can only take adimensional arguments, however functions like sqrt, cbrt, and pow can take dimensioned quantities and alter their dimensions. The library defines sqrt, sqrtf, cbrt, and cbrtf which can be used directly, and map to the corresponding functions in the std library or in CUDA.
For the function pow (or powf) the exponent (2nd argument) must be known at compile-time and so the library defines both functions with template arguments for numerator and denominator:
std::cout << pow<7>(pow<1, 7>(a)) << '\n'; // the first 'pow' is has exponent 7 while the second has exponent 1/7
std::cout << float64<Length>(5) + sqrt(float64<Area>(25)) << '\n';
std::cout << float64<Length>(5) + cbrt(float64<Volume>(125)) << '\n';
float64<Velocity> lightspeed = 1.0 / sqrt(float64<Permittivity>(8.85418781762039e-12)*float64<Permeability>(1.256637061435917e-6));
To declare new dimensions the base dimensions - Length, Time, Mass, Charge, Temperature - can be used in conjunction with the structs MUl_DIMS and DIMS_POW:
using Force = MUL_DIMS<Mass, Length, DIMS_POW<Time, -2>::value>::value; // note: 'Force' is already defined in the library
The implementation of Siano's orientational analysis is still in an experimental phase since several adaptations add to me made to accommodate the fractional powers. The types can already be defined from the base units. Since Length is the only base unit to have orientation the extra units LengthX, LengthY, and LengthZ are defined to allow the construction of any dimension and orientation.