A library that provides runtime unit values, instead of individual unit types, for the purposes of working with units of measurement at run time possibly from user input.
This software was developed for use in LLNL/GridDyn, and HELICS and is currently a work in progress (though getting close). Namespaces, function names, and code organization is subject to change though is getting more stable, input is welcome. An [in development] set of documentation is available.
A units library was needed to be able to represent units from a wide range of disciplines and be able to separate them from the numerical values for use in calculations when needed. The main drivers are converting units, often represented by strings, to a standardized unit set when dealing with user input and output. Being able to use the unit as a singular type that could contain any unit, and not introduce a huge number of types to represent all possible units. And being able to associate an completely arbitrary unit given by users with a generic interface and support conversions between those user defined units. The library has its origins in power systems so support for per-unit operations was also lacking in the alternatives.
It was desired that the unit representation be a compact type(<=8 bytes) that is typically passed by value, that can represent a wide assortment of units and arbitrary combinations of units. The primary use of the conversions is at run-time to convert user input/output to/from internal units, it is not to provide strict type safety or dimensional analysis, though it can provide some of that. This library does NOT provide compile time checking of units. The units library provides a library that supports units and operations on them where many of the units in use are unknown at compile time and conversions and definitions are dealt with at run time, and may be of a wide variety of units.
This library is an engineering library, created to represent a huge variety of units and measurements in a simple data type instead of a proliferation of templates. It supports conversion of units to and from strings. It supports mathematical operations on units and measurements which is constexpr where possible. It supports units used in a wide variety of scientific and non scientific contexts. Supports conversions between different units of the same type as well as some typical assumptions for supporting conversions of a few dissimilar types. In some cases it also has some notion of commodities, and support for existing unit standards for strings and naming.
The primary use case for the library is string operations and conversion. For example if you have a library that does some computations with physical units. In the library code itself the units are standardized and well defined. Say a velocity, internally everything is in meters per second. But there is a configuration file that takes in the initial data and you would like to broadly support different units on the input
#include <units/units.hpp>
double GetInputValueAs(const std::string &input, precise_units out)
{
auto meas=measurement_from_string(input);
return meas.value_as(out);
}
The return value can be checked for validity as an invalid conversion would result in constants::invalid_conversion or Nan so can be checked by std::isnan
or
if (!meas.units().is_convertible(out))
{
throw(std::invalid_argument);
}
sqrt or cbrt will only produce valid results if the result is integral powers of the base units. One exception is limited support for √Hz operations in measurements of Amplitude spectral density. A specific definition of a unit representing square root of Hz is available and will work in combination with other units.rad in the nature of radiation absorbed dose is one such unit as it would conflicts with rad in terms of radians. So rad means radians by default since that is the more common use in electrical engineering. The use of domains in the conversion operations can control this to some extent. For example the 'Nuclear' domain specifies that rad refers to radiation absorbed dose instead of the angle.If you are looking for compile time and prevention of unit errors in equations for dimensional analysis one of these libraries might work for you.
C++14 with no dependencies.These libraries will work well if the number of units being dealt with is known at compile time. Many also produce zero overhead operations and checking. Therefore in situations where this is possible other libraries are a preferred alternative.
There are only a few types in the library
detail::unit_base is the base representation of physical units and powers. It uses a bitfield to store the base unit representation in a 4-byte representation. It is mostly expected that unit_base will not be used in a standalone context but through one of other types.unit is the primary type representing a physical unit it consists of a float multiplier along with a unit_base and contains this within an 8 byte type. The float has an accuracy of around 6 decimal digits. Units within that tolerance will compare equal.precise_unit is the a more accurate type representing a physical unit it consists of a double multiplier along with a unit_base and contains this within an 16 byte type. The double has an accuracy of around 13 decimal digits. Units within that tolerance will compare equal. The remaining 4 bytes are used to contain a commodity object code.measurement is a 16 byte type containing a double value along with a unit and mathematical operations can be performed on it usually producing a new measurement.precise_measurement is similar to measurement except using a double for the quantity and a precise_unit as the units.fixed_measurement is a 16 byte type containing a double value along with a constant unit and mathematical operations can be performed on it usually producing a new measurement. The distinction between fixed_measurement and measurement is that the unit definition of fixed_measurement is constant and any assignments get automatically converted, fixed_measurement's are implicitly convertible to a measurement of the same value type. fixed_measurement also support some operation with pure numbers by assuming a unit that are not allowed on regular measurement types.fixed_precise_measurement is similar to fixed_measurement except it uses precise_unit as a base and uses a double for the measurement instead of a template, and it is 24 bytes instead of 16.uncertain_measurement is similar to measurement except it uses a 32-bit float for the value and contains an uncertainty field which is also 32-bits. Mathematical operations on uncertain_measurements will propagate the uncertainty and convert it as necessary. The class also includes functions for simple-uncertainty propagation like simple_subtract which just sums the uncertainties. The sum-of-squares methods is used in the overloaded math operators. Mathematical operations are supported on the type and it interoperates with measurements.The unit class consists of a multiplier and a representation of base units.
The seven SI units + radians + currency units + count units. In addition a unit has 4 flags, per-unit for per unit or ratio units. One flag[i_flag] that is a representation of imaginary units, one flags for a variety of purposes and to differentiate otherwise similar units[e_flag]. And a flag to indicate an equation unit. Due to the requirement that the base units fit into a 4 byte type the represented powers of the units are limited. The table below shows the bit representation range and observed range of use in equations and observed usage
| Base Unit | Bits | Representable range | Normal Range | Intermediate Operations |
|---|---|---|---|---|
| meter | 4 | [-8,+7] | [-4,+4] | [-6,+6] |
| kilogram | 3 | [-4,+3] | [-1,+1] | [-2,+2] |
| second | 4 | [-8,+7] | [-4,+4] | [-6,+6] |
| ampere | 3 | [-4,+3] | [-2,+2] | |
| kelvin | 3 | [-4,+3] | [-4,+1] | |
| mole | 2 | [-2,+1] | [-1,+1] | |
| candela | 2 | [-2,+1] | [-1,+1] | |
| currency | 2 | [-2,+1] | [-1,+1] | |
| count | 2 | [-2,+1] | [-1,+1] | |
| radians | 3 | [-4,+3] | [-2,+2] |
These ranges were chosen to represent nearly all physical quantities that could be found in various disciplines we have encountered.
The CMake variable UNITS_BASE_TYPE, if set to a 64 bit type like uint64_t, will double the space requirements but also change the ranges to be at least a power of 4 larger than the above table. See CMake Reference for more details.
year has different meanings in different contexts. SI defines the default year as yr=365*day=8760*hr the specific domains define it differently. 'year' means 365.25 days in the UCUM domain and the mean tropical year for the astronomy domain.i*conj(i)=i^0. This is useful for directional units such as compass directions and reactive power in power systems.>= and <= check first for > or < conditions then check for equality if needed. There are a few situations that are not totally consistent like 1.0000001*m==1.0*m and 1.0000001*m>1.0*m, but such is nature of floating point operations. So from a mathematical purity sense this isn't consistent but does mostly what was needed. If the difference between the two values is a subnormal number the equality comparison also evaluates to true.There are 2 sets of defined units, many common units are defined in the units namespace, many others are defined in units::precise and subnamespaces.
See Defined Units for details on the available units.
A set of physical and numerical constants are defined in the units::constants namespace. More details and a list of available constants are described in Physical Units. Some of the available constants that are measured vs. defined have an uncertain_measurement version available as well that includes the uncertainty.
There are two parts of the library a header only portion that can simply be copied and used. There are 3 headers units_decl.hpp declares the underlying classes. unit_defintions.hpp declares constants for many of the units, and units.hpp which is the primary public interface to units. If units.hpp is included in another file and the variable UNITS_HEADER_ONLY is defined then none of the functions that require the cpp files are defined. These header files can simply be included in your project and used with no additional building required.
The second part is a few cpp files that can add some additional functionality. The primary additions from the cpp file are an ability to take roots of units and measurements and convert to and from strings. These files can be built as a standalone static library or included in the source code of whatever project want to use them. The code should build with an C++11 compiler. Most of the library is tagged with constexpr so can be run at compile time to link units that are known at compile time. Unit numerical conversions are not at compile time, so will have a run-time cost. A quick_convert function is available to do simple conversions. with a requirement that the units have the same base and not be an equation unit. The cpp code also includes some functions for commodities and will eventually have r20 and x12 conversions, though this is not complete yet.
It builds by default with the static library. Using UNIT_BUILD_SHARED_LIBRARY or BUILD_SHARED_LIBS will build the shared library instead. Either one can be used with CMake as units::units. The header only library target is also generated units::header_only. The shared/static library has a CMake target units::units.
If you want to try out the string conversion components. There is server running that can do the string conversions
For more details see the documentation
A converter command line application can be built as part the units library by setting
UNITS_BUILD_CONVERTER_APP=ON in the CMake build. This is a simple command line script that takes a measurement entered on the command line and a unit to convert to and returns the new value by itself or part of a string output with the units either simplified or in original form. If you want to run your own converter web server, a docker container is available on dockerhub.
Many units are defined as constexpr objects and can be used directly
#include "units/units.hpp"
using namespace units
measurement length1=45.0*m;
measurement length2=20.0*m;
measurement area=length1*length2;
std::cout<<"the area is "<<area<< " or "<<area.convert_to(ft.pow(2))<<".\n";
These operations apply to units and precise_units
<unit>(<unit_data>) construct from a base unit_data<unit>(<unit_data>, double multiplier) construct a unit from a base data and a multiplier<unit>(double multiplier, <unit>) construct from a multiplier and another unit<unit> inv() generate a new unit containing the inverse unit m.inv()= 1/m<unit> pow(int power) take a unit to power(NOTE: beware of limits on power representations of some units, things will always wrap so it is defined but may not produce what you expect). power can be negative.bool is_exactly_the_same(<unit>) compare two units and check for exact equivalence in both the unit_data and the multiplier, NOTE: this uses double equalitybool has_same_base(<unit>|<unit_data>) check if the <unit_data> is the sameequivalent_non_counting(<unit>|<unit_data>) check if the units are equivalent ignoring the counting basesbool is_convertible(<unit>) check if the units are convertible to each other, currently checks equivalent_non_counting(), but some additional conditions might be allowed in the future to better match convert.int unit_type_count() count the number of unit bases used, (does not take into consideration powers, just if the dimension is used or not.bool is_per_unit() true if the unit has the per_unit flag activebool is_equation() true if the unit has the equation flag activebool has_i_flag() true if the i_flag is marked activebool has_e_flag() true if the e_flag is marked activedouble multiplier() return the unit multiplier as a double(regardless of how it is actually stored)<float|double> cround() round the multiplier to an appropriate number of digits<unit_data> base_units() get the base unitsvoid clear_flags() clear any flags associated with the unitsFor precise_units only
commodity() get the commodity of the unitcommodity(int commodity) assign a commodity to the precise_unit.There are also several operator overloads that apply to units and precise_units.
<unit>=<unit>*<unit> generate a new unit with the units multiplied ie m*m does what you might expect and produces a new unit with m^2<unit>=<unit>/<unit> generate a new unit with the units divided ie m/s does what you might expeContent type
Image
Digest
sha256:2efa438e5…
Size
29.5 MB
Last updated
9 months ago
docker pull phlptp/units:fuzzer