The purpose of this small library is to perform automatic compile-time or runtime dimensional checking when dealing with quantities and units.
There is no actual distinction between units and quantities, so there are no
distinct quantity and unit types. All operations are actually done on
quantities. For example, meter
is both the unit meter and the quantity 1m.
New quantities can be derived from other ones using operators or dedicated
functions.
Quantities can be parsed from strings at runtime and compile-time.
The main SI units and prefixes are predefined. Units with other dimensions can be defined by the user.
Copyright 2013-2015, Nicolas Sicard.
License: Boost License 1.0.
-
The library defines a
Quantity
type (a template) that represents a physical quantity, or any user-defined type of quantity. A quantity can be seen as the product of a scalar value and a vector of dimensions. -
A
Quantity
is a wrapper struct around a numeric value, where the only payload is this numeric value; no other data is stored. So the memory size of a quantity is the same as its underlying numeric type. With optimizations on, the compiler generates the same code as if normal numeric values were used.
For the moment, only built-in numeric types are handled. But it should be possible to make it work with any "number-like" type.
- Two quantities with the same dimensions share the same type (assuming the underlying numeric types are the same). Thus functions and types using quantities generally won't have to be templated if the dimensions of the quantities are known at compile time.
The main consequence of principle #3 is that all quantities sharing the same dimensions are internally expressed in the same unit, which is the base unit for this quantity. For instance, all lengths are stored as meters, which is the base unit of length. The quantity 3km is stored as 3000m, 2min is stored as 120s, etc. The drawback (possibly an important one) is that, when assigning a new value to a quantity, the binary representation is preserved only if the quantity is expressed in the base unit.
An indirect consequence is that there is no unit symbol associated with a
quantity. The only relevant symbol would have been the one of the base unit, but
it's rarely the best choice. But in practice, when formatting a quantity, the
unit is usually chosen in advance. If not, no simple algorithm is capable of
guessing the relevant unit. So I have decided that a quantity wouldn't format
itself correctly. Instead, for now, the toString
function prints the value and
the dimensions vector. To print the units properly, the user can use the
provided format helpers, or use the result of the value
method.
The package defines all the main SI units and prefixes, as well as aliases for their types.
import quantities;
import std.math : approxEqual;
import std.stdio : writeln, writefln;
void main()
{
auto distance = 384_400 * kilo(meter);
auto speed = 299_792_458 * meter/second;
Time time;
time = distance / speed;
writefln("Travel time of light from the moon: %s s", time.value(second));
}
Mass mass;
static assert(!__traits(compiles, mass = 15 * meter));
static assert(!__traits(compiles, mass = 1.2));
enum distance = 384_400 * kilo(meter);
enum speed = 299_792_458 * meter/second;
enum time = distance / speed;
writefln("Travel time of light from the moon: %s s", time.value(second));
enum inch = 2.54 * centi(meter);
enum mile = 1609 * meter;
writefln("There are %s inches in a mile", mile.value(inch));
// Create a new base unit of currency
enum euro = unit!(double, "C"); // C is the chosen dimension symol (for currency...)
auto dollar = euro / 1.35;
auto price = 2000 * dollar;
writefln("This computer costs €%.2f", price.value(euro));
At compile time:
enum distance = si!"384_400 km";
enum speed = si!"299_792_458 m/s";
enum time = distance / speed;
writefln("Travel time of light from the moon: %s s", time.value(second));
static assert(is(typeof(distance) == Length));
static assert(is(typeof(speed) == Speed));
At runtime:
auto data = [
"distance-to-the-moon": "384_400 km",
"speed-of-light": "299_792_458 m/s"
];
auto distance = parseSI!Length(data["distance-to-the-moon"]);
auto speed = parseSI!Speed(data["speed-of-light"]);
auto time = distance / speed;
writefln("Travel time of light from the moon: %s s", time.value(second));
// Use the predefined quantity types (in module quantities.si)
Volume volume;
Concentration concentration;
Mass mass;
// Define a new quantity type
alias MolarMass = typeof(kilogram/mole);
// I have to make a new solution at the concentration of 25 mmol/L
concentration = 25 * milli(mole)/liter;
// The final volume is 100 ml.
volume = 100 * milli(liter);
// The molar mass of my compound is 118.9 g/mol
MolarMass mm = 118.9 * gram/mole;
// What mass should I weigh?
mass = concentration * volume * mm;
writefln("Weigh %s of substance", mass);
// prints: Weigh 0.00029725 [M] of substance
// Wait! That's not really useful!
// My scales graduations are in 1/10 milligrams!
writefln("Weigh %.1f mg of substance", mass.value(milli(gram)));
// prints: Weigh 297.3 mg of substance
}