a compile-time, header-only, dimensional analysis and unit conversion library built on c++14 with no dependencies.

Overview

UNITS

A compile-time, header-only, dimensional analysis library built on c++14 with no dependencies.

Linux build Windows build Coverage Status license copyright language c++
msvc2015 msvc2017 gcc-4.9.3 gcc-5.4.0 clang-3.4

Get in touch

If you are using units.h in production code, I'd love to hear from you via GitHub issues!

Latest Release - v2.3.1

Get it

DOWNLOAD

New in v2.3.1

This version removes support for the Visual Studio 2013 compiler.

Features:

  • units now include constexpr name() and abbreviation() member functions, which do not really on string/iostream.
  • Builds with VS2017 Ninja generator out of the box
  • string conversions are now locale aware
  • added unary increment and decrement operators (++,--), as well as unary + operator.

Bug fixs:

  • fixed compilation error when iostream was disabled

New in v2.3.0

Features:

  • 5x compile time improvement on MSVC.
  • 1.5x compile time improvement on GCC.
  • Even more dramatic reductions in compile time can be achieved if you opt-in to specific unit definitions instead of using all the library-defined types (which is the default value). Check out Enabling a subset of units to improve compilation time for instructions.
  • Adds std::cout support for units with no defined abbreviation (they show up as a combination of SI base units)
  • Support for std::numeric_limits of unit types.
  • Assignment operators for unit types: -=, +=, /=, *=.
  • Added min and max overloads for units types in units::math.
  • Added to_string function and abbreviation functions:
    auto len = 3.5_m;
    auto str = units::length::to_string(len);
    auto abv = units::length::abbreviation(len);
    
    std::cout << str;  // prints "3.5 m"
    std::cout << abv;  // prints "m"
  • Added units of data and data transfer: bits, bytes, bits_per_second, and bytes_per_second.
  • Adds value() member for accessing underlying type.
  • Adds value_type trait, as a synonym for underlying_type.
  • Adds definitions for Julian and Gregorian years.
  • Thanks to @dinocore1, units now supports cmake install and find_packages. From the pull request:
    # To have cmake install units library to a local 'install' directory:
    mkdir build
    cd build
    cmake -DCMAKE_INSTALL_PREFIX="install" ..
    cmake --build . --target install
    # The units library can then be used in some other cmake project using 
    # the standard 'find_package' command. Like so:
    find_package(units)

Bug fixes:

  • Fixed singular name of siemen to be siemens (Thanks @Oxyd)
  • Fixed bug with cubrt operation (Thanks @PearCoding)
  • Fixed constexpr relational operators bug
  • Fixed exponential temperature conversions (Thanks @guarndt)

Tested on

  • gcc-4.9.3
  • gcc-5.4.0
  • clang-3.4
  • msvc2015
  • msvc2017

Does this library work on your compiler? If so, let me know!

Contents

Documentation

The full documentation is available here.

Description

The library consists of a single file (units.h), plus unit tests. To incorporate the library into your project, simply copy the header into a location in your include path, or add the included CMake project into your build. Using the CMake project, you can also build the unit tests and documentation if desired.

The library provides a set of types, containers, and traits to solve dimensional analysis problems, that is, problems involving dimensioned physical quantities. The conversions between units are defined as ratios at compile time, making the library incredibly fast. Additionally, specifying units as types, rather than variable suffixes (or not at all), provides complete type-safety within the compiler. This means that code that accidentally misuses units or which has errors in the dimensional analysis will fail at compile-time, not at run-time.

The unit test file unitTests/main.cpp contains example usage of every type, trait, and function contained in the library, and while not exactly user-friendly, can be a valuable resource.

Getting started guide

Add units.h to your project, along with the using directive for literals

#include <units.h>

using namespace units::literals;

Each "dimension" of unit is defined in its own namespace. See the namespaces section for a complete list. The rest of the guide assumes you've included the namespaces you plan to use:

using namespace units;
using namespace units::length;
using namespace units::time;
using namespace units::area;
using namespace units::velocity;

The easiest way to get started with the units library is to think of unit containers as double values. Unit containers are typically the units' non-plural name with the suffix _t (for type), e.g. meter_t. See the documentation for a complete list.

Units can (and should!) be used anywhere double values can be used:

double          area = 15 * 5 + 10 * 10;                // 175 m^2?
square_meter_t  area = 15_m * 5_m + 10_m * 10_m;        // 175 m^2

What makes unit types special is that unit conversions happen implicitly and automatically. Since unit conversions are evaluated at compile time, this means you can mix and match all the unit types you want with no runtime penalty.

foot_t              len   = 5_m;                            // simple implicit conversion
meters_per_second_t speed = 60_mi / 1_hr;                   // more complex implicit conversion
square_meter_t      area  = 15_m * 5_m + 1000_cm * 1000_cm; // previous example with mixed units

Note the return type has the correct dimensions of area, even though the source types were all units of length. units.h has powerful dimensional analysis capabilities. But what happens if we get the return type wrong?

meter_t  area = 15_m * 5_m + 10_m * 10_m;               // oops, m * m = m^2

E:/workspace/units/include/units.h(1405): error C2338: Units are not compatible.

Your compiler will produce an "incompatible units" error if your dimensional analysis is incorrect. If your resulting unit types are complex, you could use auto for simplicity:

auto result = 15_m * 5_m + 10_m * 10_m;                 //  m^2
auto speed  = 60_mi / 1_hr;                             //  60 mph

NOTE: Think carefully about using auto for return types. When you explicitly declare the return type, the compiler can check the dimensional analysis for correctness, and produce errors at compile time if you make a mistake. When using auto, you are basically saying that whatever unit the right-hand side of the expression results to is correct (even if it's not). If you are only using auto because a complex unit type is not available in the library, try defining a new unit as a better alternative.

More complex mathematical operations (almost every <cmath> operation actually), including exponentials and square roots are possibe by using the units::math namespace .

using namespace units::math;

meter_t a = 3_m;
meter_t b = 4_m;
meter_t c = sqrt(pow<2>(a) + pow<2>(b));    // Pythagorean threorem.

std::cout << c << std::endl;                // prints: "5 m"

Unit initialization

There are several ways to initialize unit values:

  • Explicit initialization

    meter_t distance_m(10); // Explicit initialization from double
    meter_t distance(10_m); // Explicit initialization from unit literal
    meter_t dist(100_ft);   // Explicit initialization from unit literal of a different type
  • make_unit<...>() factory. The syntax is familiar to boost::units users, and allows explicit reference to the unit type for member variable initialization.

    class myClass
    {
      public:
        
        myClass() : m_speed(make_unit<miles_per_hour_t>(100)) {}
    
      private:
    
        miles_per_hour_t m_speed;
    };

Unit tags

Unit tags are the foundation of the unit library. Unit tags are types which are never instantiated in user code, but which provide the meta-information about different units, including how to convert between them, and how to determine their compatibility for conversion.

All unit tags are defined in namespaces under the units namespace, such as units::length or units::angle, to avoid name clashes between units of different physical quantities which share the same names (like pounds). SI base units are defined as "categories" in the unit namespace.

Units are defined in terms of

  1. A scale factor relative to a base unit type.
  2. A base unit
  3. [optionally] a scale factor of pi
  4. [optionally] a datum translation (such as the +/- 32 required to convert between fahrenheit and celsius)

All units have their origin in the Système International (SI) base unit system. A special exception is made for angle units, which are defined in SI as ( m * m^-1), and in this library they are treated as a basic unit type because of their important engineering applications.

Example: the definitions of some common length units are:

namespace length
{
	using meters = units::unit<std::ratio<1>, units::category::length_unit>;	// meters are (1) unit of length in the SI system.
	using feet = units::unit<std::ratio<381, 1250>, meters>;					// feet are 0.3048 meters.
}

Unit containers

Unit containers are the primary classes which will be instantiated in user code. They can be thought of as essentially equivalent to a double, except that they have unit type tags associated with them. They can be used wherever a double would be used to store a dimensioned quantity. Containers are derived from the unit_t class, and have the form [unitname]_t, e.g. meter_t or radian_t.

Unit containers are defined in terms of the units they represent, their underlying type, and an optional non-linear scale (think decibels or Richter scale). For example, meter_t would be defined:

using meter_t = units::unit_t<units::length::meter, double, units::linear_scale>

or simply

using meter_t = units::unit_t<units::length::meter>

since the underlying type and scale parameters default to double and linear_scale respectively.

Units of compatible types (e.g length units) can be implicitly converted/assigned to one another. Units (with the exception of dimensionless types) cannot be implicitly converted to/from built-in types, such as double.

Units are constructed from built-in types, and the toDouble() method (or operator()) can be used to retrieve a built-in type value. That said, the user should prefer to operate within the unit type-space as much as is practical, and wrappers of most <cmath> functions are provided to enable operating solely in the unit_t domain.

The primary purpose of unit containers is to provide type safety and dimensional analysis for mathematical operations. for instance, the velocity of an object can be calculated:

auto objectVelocity = meter_t(100.0) / second_t(2.0);

The resulting velocity type will be deduced to be velocity::meters_per_second with a value of 50.0. Additionally, if the return type if specified, the type system will verify that the units are compatible. For example, the following will fail to compile:

units::velocity::meters_per_second objectVelocity = square_meter_t(100.0) / second_t(2.0); // Error: Unit types are not compatible.`

Unit containers can (and should!) be used to perform implicit conversions:

units::time::second_t a;
units::time::minute_t b(1.0);

a = b;	// a == 60.0

Arithmetic can be performed on unit containers the same way it can for built-in types. However, unlike built-in types, the return value of unit-type arithmetic will be the proper unit to represent the resulting quantity.

using namespace units::length;
using namespace units::area;

meter_t a_m(1.0), b_m(2.0), c_m;
foot_t	a_ft(1.0), b_ft(2.0), c_ft;

c_m = a_m + b_m;                            // OK. c == 3m
c_ft = a_m + b_m;                           // OK. resulting 3m is converted to ft.
auto result = a_m + b_ft;                   // OK. result is `meter_t` (left-most unit)

auto result_sm = a_m * b_m;                 // OK. result_sm is `square_meter_t`.
auto result_s = a_m / b_m;                  // OK. result_s is `dimensionless_t`.
auto result = a_m * b_ft;                   // OK. result is `square_meter_t` (left-most unit)

auto result = a_m * square_meter_t(1.0);    // OK. units can always be multiplied. Result is `cubed<meter_t>`.
auto result = a_m * scalar_t(1.0);          // OK. units can always be multiplied. Result is `meter_t`.

Unsupported arithmetic, or improper return types will result in compiler errors:

c_m = a_m + 5.0;                            // Error. can't add scalars to dimensioned units.
c_m = a_m + scalar_t(5.0);                  // Error. can't add scalars to dimensioned units.
auto result = a_m + square_meter_t(1.0);    // Error. Incompatible units.

By providing explicit return types for unit functions, the compiler can be used to verify the accuracy of the dimensional analysis, and thus avoiding costly errors.

Unit Literals

If you are using a compiler which supports user-defined literals (e.g. not Visual Studio 2013), then unit literals can be a convenient way to initialize and work with unit values:

using namespace units::literals;

meter_t dist	= 10_m;		// 10 m
meter_t dist2	= 1_km;		// 1000 m

Literals can also be used for any temporary values in calculations, making them more readable:

auto area = units::length::meter_t(5) * units::length::meter_t(10);	// without literals
auto area = 5_m * 10_m;							// with literals

All literals* are defined by their SI abbreviation preceded by an underscore, e.g. _m for meter. "Square" units are preceded by _sq, e.g. _sq_m for square meters. Non SI units use their most common abbreviations.

All literals are defined in the units::literals namespace, and in order to use literals in your code you must include the line using units::literals (since there is no way to put a namespace on an operator).

* with the exception of Teslas, which use _Te for compatibility with MSVC compilers.

<cmath> Functions

The units library include type-safe unit_t container wrappers for almost all of the <cmath> functions, including the c++11 extensions. These functions can be found in the units::math namespace. The units library versions don't conflict with <cmath>, and it's possible to use both libraries in the same code.

The overloaded functions ensure that only the proper unit types are accepted into the functions, and that the return value type matches the expected units, all without needing to result to the type-unsafe toDouble() member.

In rare cases, the overload resolution for a given type may be ambiguous. If so, simply prepend the function with the fully-qualified units::math prefix, e.g.

meter_t x(2.0);
meter_t y(3.0);
square_meter_t z(1.0);
square_meter_t result;

result = fma(x, y, z);												// Error: ambiguous
double result = fma(x.toDouble(), y.toDouble(), z.toDouble());		// Warning: Unsafe!
result = math::fma(x, y, z);										// OK.

Exponentials and Square Roots

Many functions require units to be raised to some power. This can be accomplished using the units::math::pow function:

square_meter_t m2 = units::math::pow<2>(meter_t(5.0));	// m2 == 25.0

The only constraint is that the exponential power (given in the template argument) must be known at compile time, so that the type system can deduce the output type. This differs from the <cmath> pow implementation, which takes exponent values at runtime.

Square roots are also provided with the units::math::sqrt function. Due to the nature of the sqrt operation, the units library can often provide exact conversions for square root operations, but not in every case. The rest of the time, the sqrt unit will be a rational_approximation of the real value. These are guaranteed to be accurate to at least 10 decimal places.

meter_t m = units::math::sqrt(square_meter_t(4.0));		// m == 2.0

Removing type safety

When interfacing with APIs, libraries, and frameworks which aren't unit enabled, it may be necessary (if regrettable) to remove the type-safety of a unit container and expose its underlying type. This is possible using the unit_cast function, or the to<> member function.

using namespace units;
using namespace units::length;

// Get double value from a unit container (double is the default underlying type of the units library)
meter_t dist(10);
double dval = unit_cast<double>(dist);
double dval2 = dist.to<double>();

// Get integer value (potentially narrowing, be careful!)
int ival = unit_cast<int>(dist);
int ival2 = dist.to<int>();

Both functions produce the same results, the choice of syntax is simply a user preference.

To determine the underlying type of the unit container, the (verbose) trait units::traits::unit_t_traits<decltype(dist)>::underlying_type could be used.

Efficiency

Complex, recursively-defined conversions are performed in just 5 instructions:

	year_t twoYears(2.0);
	week_t twoYearsInWeeks = twoYears;
00007FF7BDB57FF6  xorps       xmm9,xmm9  
00007FF7BDB57FFA  cvtsi2sd    xmm9,rax  
00007FF7BDB57FFF  mulsd       xmm9,mmword ptr [[email protected] (07FF7BDBB31A0h)]  
00007FF7BDB58008  divsd       xmm9,mmword ptr [[email protected] (07FF7BDBB33C0h)]  
00007FF7BDB58011  movsd       mmword ptr [rbp+6Fh],xmm9  
	EXPECT_EQ(week_t(104.286), twoYearsInWeeks);
00007FF7BDB58017  ...

In the library, the year to week conversion is defined in terms of years -> days -> hours -> minutes -> seconds -> minutes -> hours -> days -> weeks but the total conversion ratio is computed at compile-time and the math is optimized to two floating-point operations.

Unit conversions between equivalent types are optimized away completely, and generate no machine code.

Pure Compile-time Unit Manipulation

In many cases, unit equations are used to determine derived values from a set of values which are known at compile-time. In these situations, it would be optimal to pre-compute the derived values at compile time, thus generating no machine code and incurring no run-time penalty.

The unit_value_t class is the mechanism in the units library to perform compile-time arithmetic. The unit_value_t class functions exactly the same way as std::ratio, but with an associated unit tag and the ensuing type safety.

For a simple example, let's define a right triangle whose hypotenuse is the sum of the squares of its side (a Pythagorean triple)

struct RightTriangle
{
	using a = unit_value_t<meters, 3>;
	using b = unit_value_t<meters, 4>;
	using c = unit_value_sqrt<unit_value_add<unit_value_power<a, 2>, unit_value_power<b, 2>>>;
};

The definition above is perfectly efficient, as it generates no run-time code whatsoever, and still provides all the type safety of unit containers. The values of a, b, and c can be accessed at runtime using the static value() method of unit_value_t

auto a = RightTriangle::a::value();	// a is `meter_t(3)`
auto b = RightTriangle::b::value();	// b is `meter_t(4)`
auto c = RightTriangle::c::value();	// c is `meter_t(5)`

The available compile-time operations are:

  • units::unit_value_add
  • units::unit_value_subtract
  • units::unit_value_multiply
  • units::unit_value_divide
  • units::unit_value_power
  • units::unit_value_sqrt

Conversion without unit containers

The preferred method of conversion is implicitly though the use of unit containers, however unit conversion can be accomplished using units::convert for arithmetic types:

double val_in = convert<feet, inches>(1.0);	// val_in == 12.0

For type-safe conversion, prefer implicit conversion via unit_t type containers..

Namespaces

Unit tags and containers are split into separate namespaces to avoid conflicting unit names which represent different physical quantities.

Unit tag and unit_t container definitions are defined in the following namespaces:

  • units::length
  • units::mass
  • units::time
  • units::angle (plane)
  • units::current
  • units::temperature
  • units::substance (amount of, i.e. moles)
  • units::luminous_intensity
  • units::solid_angle
  • units::frequency
  • units::velocity
  • units::angular_velocity
  • units::acceleration
  • units::force
  • units::pressure
  • units::charge
  • units::energy
  • units::power
  • units::voltage
  • units::capacitance
  • units::impedance
  • units::magnetic_flux
  • units::magnetic_field_strength
  • units::inductance
  • units::luminous_flux
  • units::illuminance
  • units::radiation
  • units::torque
  • units::area
  • units::volume
  • units::density
  • units::concentration
  • units::data
  • units::data_transfer_rate
  • units::constants (scalar and non-scalar physical constants like Avogadro's number)

Literal values for unit containers are defined in the literals namespace

  • units::literals

Mathematical operations like sin, log, floor, etc are defined in the following namespaces:

  • units::math

Type traits that you can use to test unit types are defined in the following namespaces:

  • units::traits

Defining new units

The units library strives to provide built-in types for every conceivable unit, and before defining your own units you should double-check the namespaces to make sure it's not already included. That said, if you need to roll your own units, the library is extensible by design.

Defining new units is simple, as they can be recursively defined as ratio of previously-defined units in a way that mimics natural language and is highly readable:

namespace time
{
	using seconds = units::unit<std::ratio<1>,   units::category::time_unit>;
	using minutes = units::unit<std::ratio<60>,  seconds>;
	using hours   = units::unit<std::ratio<60>,  minutes>;
	using days    = units::unit<std::ratio<24>,  hours>;
	using weeks   = units::unit<std::ratio<7>,   days>;
	using years   = units::unit<std::ratio<365>, days>;
}

Units are defined in the form: using [unit] = unit<std::ratio<[number of base units per unit]>, [base unit]>;, where:

  • the [unit] is what you are defining.
  • the [base unit] is the unit that [unit] will be defined in terms of, and
  • the [number of base units per unit] is the conversion ratio between the two, expressed as a std::ratio type.

Compound units are defined in a similar manner, with additional helper functions for polynomials:

using acceleration = compound_unit<meters, inverse<squared<seconds>>>;		// (m / s^2)

The available helpers are:

  • units::inverse<...> (inverts the unit, e.g. meters becomes meters^-1, or 1 / meters)
  • units::squared<...> (squares the unit, e.g. meters becomes meters^2)
  • units::cubed<...> (cubes the unit, e.g. meters becomes meters^3)
  • units::square_root<...> (takes the square root of the unit, e.g meters^2 becomes meters)
  • units::atto<...> through units::exa<...> metric prefixes

Unit definition macros

Version 2.1.0 of the units library simplifies the task of adding new units by introducing a set of macros for unit definitions:

  • UNIT_ADD(namespaceName, nameSingular, namePlural, abbreviation, definition)

    This macro adds a single new unit to the given namespace, as well as a literal definition and cout support based on the given abbreviation. e.g.

    UNIT_ADD(length, foot, feet, ft, unit<std::ratio<381, 1250>, meters>)

    Would create the units::length::feet tag, the units::length::foot_t container type, and the _ft literal.

  • UNIT_ADD_WITH_METRIC_PREFIXES(namespaceName, nameSingular, namePlural, abbreviation, definition)

    This macro has the same functionality as UNIT_ADD, but additionally adds unit types with all metric prefixes from femto to peta (smaller and larger prefixes mostly result in arithmetic overflow).

  • UNIT_ADD_WITH_CUSTOM_TYPE(namespaceName, nameSingular, namePlural, abbreviation, underlyingType, definition)

    This macro has the same functionality as UNIT_ADD, but additionally adds an underlyingType parameter, which can be used to create units with integral, or other underlying types. The library default underlying type is double.

  • UNIT_ADD_DECIBEL(namespaceName, nameSingular, abbreviation)

    Adds the decibel representation for a previously-defined unit. e.g.

    UNIT_ADD_DECIBEL(power, watt, dBW)

    Adds the dBW_t container, and the _dBW literal.

  • UNIT_ADD_CATEGORY_TRAIT(unitCategory, baseUnit)

    This macro creates a type-trait to check whether a unit is of a certain category, e.g. length. This is only necessary if defining new categories of units which are not included in units.h at all. e.g.

    UNIT_ADD_CATEGORY_TRAIT(length, meter)

    Adds the units::traits::is_length_unit trait.

Unit Type Traits

The units library provides a comprehensive set of type-traits, which can be used in templated user code to enforce that the unit types have certain properties.

For example, let's say you want to write a function that validates that the square footage of an office (given in any units), meets the minimum size required by local ordinance.

template<typename Units>
bool isMinimumSize(Units x)
{
	return x >= square_feet_t(80.0);
}

This function will fail to compile if Units is not a unit of area (since incompatible unit types are not comparable), but it will produce a series difficult-to-understand template errors. Type traits could be used to make the error message more friendly:

template<typename Units>
bool isMinimumSize(Units x)
{
	static_assert(units::traits::is_area_unit<Units>::value, "Input value x must represent an area quantity.");
	return x >= square_feet_t(80.0);
}

See the units::traits namespace for a list of all the supported traits.

Changing the underlying type of unit_t

The default underlying type for all unit containers is double. However, this can be overridden by providing a definition for UNIT_LIB_DEFAULT_TYPE, e.g.

// Use 64-bit integers as the underlying unit type
#define UNIT_LIB_DEFAULT_TYPE int64_t
#include <units.h>

NOTE: changing the underlying type may result in unexpected behavior. Unit conversion makes heavy use of division, which may make integral types unsuitable except for niche embedded applications. Using excessively large types may increase the number of arithmetic overflow errors.

Disabling IOStream

For some embedded applications, it may be desirable to remove all references to <iostream> in order to reduce compiled binary size and RAM requirements. There are two ways to accomplish this:

  1. If you are copy/pasting units.h into your project include directory, then simply define UNIT_LIB_DISABLE_IOSTREAM before including the header.

    #define UNIT_LIB_DISABLE_IOSTREAM
    #include <units.h>
  2. If you are including units in your project as a CMake target (using add_subdirectory), then all you need to do is set the DISABLE_IOSTREAM cache option, either using the cmake-gui, or by adding the option to the cmake command line during configuration:

    cmake -DDISABLE_IOSTREAM=ON -DBUILD_TESTS=OFF ..
    cmake --build . --config Release

Enabling a subset of units to improve compilation time

If you know that you only need a subset of the unit namespaces for your application, you can dramatically improve compilation time by disabling the default definitions, and then only opting-in to the namespaces you want. For example:

// Only use length and time
#define DISABLE_PREDEFINED_UNITS
#define ENABLE_PREDEFINED_LENGTH_UNITS
#define ENABLE_PREDEFINED_TIME_UNITS

The generic algorithm is

  1. disable the pre-defined units using #define DISABLE_PREDEFINED_UNITS
  2. opt-in to the namespaces you want using #define ENABLE_PREDEFINED_<namepsace name>_UNITS

Additionally, for CMake users, there are equivalently-named cmake options defined which will automatically include the preprocessor definitions in your project.

Macro clashes

With certain compilers, it is possible that system header files like <ctype.h> will define macros which conflict with the unit literals, which use SI abbreviations. In these cases, it is general safe and advisable to #undef the offending macros.

Windows macros

_T is known to conflict, but is hardcoded into the compiler and can't be disabled. For this reason, Tesla units use the _Te abbreviation.

The following macros may need to be undefined on the Windows platform to use units:

#undef pascal
#include <units.h>

ARM macros

The following macros may need to be undefined on the ARM platform to use units::literals:

#undef _U
#undef _L
#undef _N
#undef _S
#undef _P
#undef _C
#undef _X
#undef _B
#define UNIT_LIB_DISABLE_IOSTREAM // it's prudent to disable IOStream on embedded platforms as well.
#include <units.h>

It's best to undefine macros on an as-needed basis.

CMake Instructions

There are several ways to incorporate units.h into your project. The simplest is to just copy include/units.h into your project include directory (which the licensing allows you to do). However, you'll have to properly set up the necessary compilation flags for C++14 (-std=c++14 on gcc).

However, if you are already using CMake as your build system, the recommended way to include units is to copy the entire units project as a subdirectory within your own top-level project folder. Then, in your CMakeLists.txt file add

add_subdirectory(units)

add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} units)

Also, if you are distributing headers that depends on units.h, you shoud consider using cmake's find_package to check if the header is installed on the user's system:

```cmake
find_package(units)

add_library(${PROJECT_NAME} my_lib.cpp)
target_link_libraries(${PROJECT_NAME} units::units)
```

The include path properties are part of the units target, so adding it as a subdirectory and linking against it is all you need to do, no need to worry about additional include directories.

If you don't care about the unit tests, you can minimize compile time by invoking CMake with the following option:

cmake -DBUILD_TESTS=OFF ..
cmake -build .

Build Instructions

The library itself consists of a single header units.h, and can be included into your project without being built.

The unit tests and documentation can be built with CMake. A doxygen installation is required to generate the documentation, and a Tex install is needed if pdf documentation is desired.

To build the tests:

Windows

  1. Ensure cmake is installed, and that the bin directory is in your %PATH% variable, and that a compiler like Visual Studio 2015 Community Edition is installed.
  2. clone the repository or download the .zip package.
  3. Open a cmd terminal and navigate to the source directory.
  4. Type the following commands:
  • md build
  • cd build
  • cmake -Wno-dev ..
  • cmake --build . --config Release
  1. The tests will be created in an executable called unitLibTest.exe in the folder build/unitTests/Release.

Linux

  1. Ensure you are using cmake 3.2 or later. You can verify this with cmake --version.
  2. Ensure you are using gcc version 4.9 or greater. You can verify this with gcc --version.
  3. clone the repository or download the .tar.gz package.
  4. Open a terminal and navigate to the source directory.
  5. Type the following commands:
  • mkdir build
  • cd build
  • cmake -Wno-dev ..
  • cmake --build . --config Release
  1. The tests will be created in an executable called unitLibTest in the folder build/unitTests.

Previous Releases

  • v2.0.3
    • unit_t types are now trivial types.
    • unit_t types support the unary minus (negation) operator.
    • Compile-time unit arithmetic via unit_value_t.
    • Unit-enabled ports of most <cmath> functions, including c++11 extensions.
    • Square-root manipulators for unit, unit_t, and unit_value_t.
    • Improved documentation.
  • v1.3.0
    • Adds ostream support.
    • bug fixes.
  • v1.2.2
    • Bug fixes (#1) and namespace cleanup.
  • v1.2.0
    • Adds angular velocity units.
  • v1.1.1
    • Adds Doxygen and additional type traits.
  • v1.0.0
    • Initial release.
Issues
  • Strengthen unit aliases

    Strengthen unit aliases

    Discussed in #138.

    This is a hack to get a feeling of how it would be to make the units actual types and not aliases. This would allow us to use CTAD (which isn't looking hopeful for type aliases in C++20) and better error messages.

    My observations:

    • The library adds a lot of symbols. I've read about linkers having problems with over 2^16 symbols. This change adds many more.
    • This is WIP. e646b14, being the hack it is, adds redundant declarations to get things to work. More symbols.
    • Unfortunately, the way std::common_type and std::hash work require us to add more symbols (specializations) for each strong unit alias.
    • The tests pass when I change uses of std::is_same_v for has_equivalent_unit_conversion.

    While working on this, I'll see if I can reduce the amount of symbols the library produces. Making is_unit what is_derived_from_unit (added in this hack) is and taking into account everywhere of this new definition should help there.

    Foreseeable future work includes preserving the strong unit alias whenever possible. As it is, it converts to its base ASAP, losing on the advantage of better error messages. And, of course, making the strong unit alises templated entities.

    I just wanted to get this hack out. I'll make it a proper work later. As such, much of this message will be out-of-date and edited out.

    opened by JohelEGP 36
  • Sparser unit templates, possible?

    Sparser unit templates, possible?

    This units library generates many complicated templates during compilation, which can greatly slow compilation (notably under Clang) and produces excessively verbose error messages. The latter is especially concerning, because the main reason to use the units library is its ability to report errors in dimensional analysis to the user.

    At the root of the problem is a template design that uses an exhaustive set of parameters to describe a unit. This set comprises, among other things, a rational scale, and eight rational exponents which corresponding to a fixed list of approved base dimensions — subject to expansion as new options demonstrate their utility. The lengthy error messages describing these templates must be carefully examined to determine the fault in the user's dimensional analysis, taking into account the standard order of approved base dimensions.

    base_unit_multiply<meter_unit, inverse_base<time_unit>> =
    units::base_unit<
        std::ratio<1, 1>,
        std::ratio<0, 1>,
        std::ratio<-1, 1>,
        std::ratio<0, 1>,
        std::ratio<0, 1>,
        std::ratio<0, 1>,
        std::ratio<0, 1>,
        std::ratio<0, 1>>
    

    In my frustration I have contemplated making a branch of the library that uses integers instead of ratios as dimensional exponents, to improve error readability and compile time (and because I foresee no use for fractional dimensions in my math).

    units::base_unit<1, 0, -1, 0, 0, 0, 0, 0>
    

    I have also seen that boost::units tackles another part of this problem by allowing the user to define different "systems" comprising chosen subsets of library- and user-defined units. Both of these are only partial solutions to the wider problem.

    What I'm curious about is whether, with the latest C++ facilities, it's possible to implement base_unit such that any transformations thereof are consolidated to a simplified representation that associates base units with their exponents. That is, something like the following:

    base_unit_multiply<meter_unit, inverse_base<time_unit>> =
    units::base_unit<
        units::dimension_whole<dimensions::length, 1>,
        units::dimension_whole<dimensions::time,  -1>>
    
    sqrt_base<acceleration_unit> =
    units::base_unit<
        units::dimension_ratio<dimensions::length, std::ratio<1, 2>>,
        units::dimension_whole<dimensions::time,   1>>
    

    or,

    units::base_unit<
        dimensions::length, std::ratio< 1, 1>,
        dimensions::time,   std::ratio<-1, 1>>
    

    Two types of simplification could be considered;

    • Associatively represent dimensions, eliminating those with an exponent of zero.
    • Reduce rational exponents to integers.

    Obviously this is all hypothetical, would require a rewrite of the library, and may be prohibitively difficult or impossible under the current C++ spec. I'm interested in understanding the barriers involved and what improvements might be feasible on this front. I won't rule out trying my own hand at some of these improvements.

    question 
    opened by EvanBalster 16
  • Redefining angle for different contexts

    Redefining angle for different contexts

    I have a situation where I would like to define different angle types that are context-dependent, e.g. a_radian and b_radian. Say there are different functions taking an angle as argument: void ComputeWithAngleA(a_radian_t foo) and void ComputeWithAngleB(b_radian_t foo). So albeit both a_radian_t and b_radian_t are technically angles, a_radian_t must not used in the context of ComputeWithAngleB(...) and vice versa. I hope this makes sense to some of you :D

    I'm having issues defining these new angle units using this program, see below for questions on 1, 2, 3:

    // (1)
    #define UNIT_LIB_DISABLE_IOSTREAM
    #include "units.h"
    
    using namespace units;
    using namespace units::literals;
    
    UNIT_ADD(strict_angle, a_radian, a_radians, arad, unit<std::ratio<1>, angle::radians>)
    UNIT_ADD(strict_angle, b_radian, b_radians, brad, unit<std::ratio<1>, angle::radians>)
    
    double ComputeSine(strict_angle::a_radian_t alpha) {
      return math::sin(alpha);
    }
    
    int main() {
      strict_angle::a_radian_t a_angle(0.1);
      // (2)
      strict_angle::b_radian_t b_angle(0.5 * constants::pi.to<double>());
      // (4)
      //strict_angle::b_radian_t b_angle = 0.5_brad;
    
      std::cout << ComputeSine(a_angle) << std::endl;
      // (3)
      std::cout << ComputeSine(b_angle) << std::endl;
    
      return 0;
    }
    

    (1) Without the definition, I get an error in the second UNIT_ADD about the redefinition of the operator<<. Can I work around this?

    (2) Without the conversion to double, I get an error "Units are not compatible." Can I avoid this?

    (3) Why does this compile? That's exactly what I don't want to happen - passing a b_radian_t angle to a function that accepts a a_radian_t. EDIT: probably works because the types are compatible...

    (4) EDIT: Getting the error 'unable to find numeric literal operator""_brad'. What am I missing here?

    Grateful for any help :) Maybe an entirely different approach can solve this.

    opened by pylipp 15
  • Support integral underlying types

    Support integral underlying types

    Resolves #125.

    • [x] Prevent lossy initialization of units from arithmetic types.
    • [x] Prevent lossy conversions and truncation of units in its converting constructor.
    • [x] Provide an alternative to convert that carries intermediate computations in the widest representation and has a type safe interface.
    • [x] Specialize std::common_type for units of the same dimension. The common type should be the least precise unit that both units can be exactly converted to.
    • [x] Implement the modulus operators with the new framework.
    • [x] Use the new framework in the equality operators.
    • [x] Use the new framework in the relational operators.
    • [x] Use the new framework in the arithmetic linear operators.
    • [x] Use the new framework in the arithmetic decibel operators.
    • [x] Use the new framework in the cmath functions.

    Along the way, some of the supporting traits and other utilities might need to be changed to support mixed underlying types.

    opened by JohelEGP 14
  • Feature implementation: variadic dimensions list

    Feature implementation: variadic dimensions list

    Hello, Nic —

    A while ago I implemented the "sparse dimensions list" concept in a fork of the units lib. Recently I've revisited that, and finally got it up to date with units v2.3.0. This branch supports all (?) the existing features of units, and passes all its unit tests, including a few new ones I've added:

    >> https://github.com/EvanBalster/units/tree/eb-variadic-dimensions

    It needs a bit of cleanup and your review before a pull request would be appropriate. In particular, some naming conventions should be adjusted, and the diff is behaving strangely in spite of the limited scope of these changes.

    Feature Overview

    Rather than using a pre-defined system of dimensions, base_unit now uses a sparse compile-time ordered list of dimensions and exponents (similar to a std::map) to express its base physical dimensions and their exponents. Only dimensions with non-zero exponents appear in the list.

    Sparse unit templates make unit definitions shorter and more self-documenting:

    // units v2.3.0
    typedef base_unit<std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<2>,
        std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>>
        luminous_flux_unit;
    
    // eb-variadic-templates
    typedef base_unit<d::angle,  std::ratio<2>, d::luminous_intensity, std::ratio<1>>
        luminous_flux_unit;
    

    This format also results in shorter, clearer error messages (my original motivation).

    Users may define their own base dimensions (e.g. qubits, hit points, dollars) and combine these with the ones provided by units without any modification to the library. It is also much easier to create modified versions of the units library with different sets of base dimensions.

    Implementation

    The new base_unit template uses an alternating variadic template parameter list of the form <base_dimension, exponent, base_dimension, exponent...>, and requires this list to be well-formed using compile-time assertions:

    • Base dimensions must derive from units::detail::_base_dimension_t.
    • Each base dimension must be followed by a valid, non-zero std::ratio exponent.
    • Base dimensions must appear in a canonical order, with no duplications.

    Each base dimension must be declared with a unique integer ID constant which determines its place in the canonical order. (base_unit does not allow two different dimensions with the same ID to be used.)

    Templates that interact with base_unit are rewritten to accommodate the new template parameter format. The greatest increase in complexity is to base_unit_multiply_impl, which filters through both inputs, inserting, combining and canceling exponents as needed. Some other functions such as base_unit_pow_impl were also rewritten.

    ostream output was rewritten to query each base dimension for a "base unit symbol" string, rather than including code for every defined base dimension.

    opened by EvanBalster 14
  • C++17 ideas?

    C++17 ideas?

    List of ideas for ways C++17 could improve the library:

    • nested namespace definitions?
    • UTF-8 literals? (degree symbol, unit power superscripts)
    • refactor some templates as folding expressions?
    • compatibility w/ std::data?
    • replace some template metaprogramming with compile-time static if (if constexpr(...))?
    • use std::variant as default underlying type? (I actually think this is probably a bad idea, since it would force users to be very pedantic about how unit_t initialization was done)

    Things to consider:

    • current state of compiler support

    How can I help?

    Point out somewhere in the library where you think a c++17 feature would simplify or enhance to code, in the form of an issue or a patch.

    help wanted question 
    opened by nholthaus 14
  • Some single-letter symbols are already defined on GCC-ARM

    Some single-letter symbols are already defined on GCC-ARM

    On GCC-ARM, ctypes.h apparently already defines _N, _C, _S and _L. Due to this, dropping units.h in an embedded ARM project causes build errors.

    I was able to get it to compile by adding:

    #undef _N
    #undef _C
    #undef _S
    #undef _L
    

    Though I'm not sure it's a good solution.

    There's a stackoverflow post from 2 years ago with exactly the same problem: http://stackoverflow.com/questions/23365562/compilation-failure-with-simple-user-defined-literal

    The offending ctype.h is here It defines:

    #define	_U	01
    #define	_L	02
    #define	_N	04
    #define	_S	010
    #define _P	020
    #define _C	040
    #define _X	0100
    #define	_B	0200
    

    so we can expect more clashes.

    wontfix ready to close 
    opened by pvaibhav 10
  • Construct from string or operator>> (deserialization)

    Construct from string or operator>> (deserialization)

    While the compile-time UDL initialization is nice, reading values from streams at run-time (e.g. from a configuration file) would be quite useful. Do you plan anything of the sort?

    enhancement 
    opened by le-migou 9
  • value() and value_type

    value() and value_type

    Migrating from boost.units would be somewhat easier if there would be a value_type defined in unit_t(which is identical to the underlying_type), as well as a .value() function that returns the underlying value.

    ready to close 
    opened by JaapAap 9
  • cout << 2.0_m * 2.0_m * 2.0_m * 2.0_m << endl;

    cout << 2.0_m * 2.0_m * 2.0_m * 2.0_m << endl;

    The following:

    cout << 2.0_m << endl;
    
    cout << 2.0_m * 2.0_m << endl;
    
    cout << 2.0_m * 2.0_m * 2.0_m << endl;
    

    outputs (as expected):

    2 m
    4 sq_m
    8 cu_m
    

    However, the following:

    cout << 2.0_m * 2.0_m * 2.0_m * 2.0_m << endl;

    outputs simply:

    16

    with no units identifier.

    enhancement ready to close 
    opened by dharmatech 9
  • How to get unit abbreviations? + strange abbrevations?

    How to get unit abbreviations? + strange abbrevations?

    Hi!

    I'm getting used to the units library and try to get out the unit abbreviation - but I can't figure out how. I know that I can do

    millimeter_t myvariable(42); cout << myvariable << endl;

    to output 42 mm - but how can I get only the abbreviation? I would need that for my own visualization which needs value and abbreviation in two different variables.

    Another question: Some units have "strange" abbreviations like degC and um instead of °C and µm. What's the reason not to use these (e.g. in utf-8 encoding)? Would look much better in visualization.

    ready to close 
    opened by stefan-muc 8
  • Slug definition appears to be off by a few parts per billion

    Slug definition appears to be off by a few parts per billion

    Please include the following information in your issue:

    1. Which version of units you are using: 2.3.1, although this problem exists on all versions, including 3.x.

    2. Which compiler exhibited the problem (including compiler version): All of them: it's a wrong definition in the code.


    A slug is defined as the mass accelerated by 1 ft/s^2 by a net force of one lbf. This works out to some exact number of kilograms, which we can find using the following other exact definitions:

    • Standard gravitational acceleration is exactly 9.80665 m/s^2.
    • 1 foot is exactly 0.3048 m.
    • 1 pound mass is exactly 0.45359237 kg.

    When I simplify this, I get that a slug is exactly (8896443230521 / 609600000000) kg, which works out to 14.5939029372063648293963... (etc.).

    However, the units library slug is defined as exactly 14.5939029 kg. This amounts to an error of a few parts per billion. The error propagates to related units, e.g., foot-pounds of torque.

    I think you could fix this: the numerator and denominator both fit into a std::intmax_t for std::ratio. However, since you're using std::ratio to represent unit magnitudes, instead of a vector space representation, you'll probably be more vulnerable to overflow when defining related units.

    bug 
    opened by chiphogg 2
  • Fix incorrect math function output for scaled dimensionless types

    Fix incorrect math function output for scaled dimensionless types

    As reported in issue #284, some functions produce incorrect results when passed certain dimensionless quantites. For example, this program from the issue, slightly modified for brevity and to try to make a later point clearer:

    #include "units.h"
    #include <iostream>
    #include <cmath>
    using namespace units::literals;
    int main()
    {
        const auto c = 1.0_um * (-1 / 1.0_m);
        std::cout << "c                   : " << c << std::endl ;
        std::cout << "units::math::exp(c) : " << units::math::exp(c) << std::endl ;
        std::cout << "std::exp(c.value()) : " << std::exp(c.value()) << std::endl ;
        return EXIT_SUCCESS ;
    }
    

    Outputs:

    c                   : -1e-06
    units::math::exp(c) : 0.367879
    std::exp(c.value()) : 0.999999
    

    value() is basically to<>(), but with the template parameter hard-coded to underlying_type, and to<>() is one of the documented ways to pass something to a non-unit-enabled API, so getting different results this way is rather surprising.

    The proximate cause of this difference is the use of operator()() instead of value() or to<>() by (some?) functions in units::math. In the case of the example above:

    template<class ScalarUnit>
    dimensionless::scalar_t exp(const ScalarUnit x) noexcept
    {
        static_assert(traits::is_dimensionless_unit<ScalarUnit>::value, "Type `ScalarUnit` must be a dimensionless unit derived from `unit_t`.");
        return dimensionless::scalar_t(std::exp(x()));
        // operator()() instead of value()/to()  ^^
    }
    

    The use of operator()() means that std::exp() is fed a different input than when value() is used, as demonstrated by the output from an appropriately modified version of the example program:

    c                   : -1e-06
    c()                 : -1
    c.value()           : -1e-06
    units::math::exp(c) : 0.367879
    std::exp(c.value()) : 0.999999
    

    However, this problem does not for all possible inputs. If the expression for c is changed as such:

    // const auto c = 1.0_um * (-1 / 1.0_m);
    const auto c = -1.0_um / 1.0_m;
    

    The issue appears to vanish:

    c                   : -1e-06
    c()                 : -1e-06
    c.value()           : -1e-06
    units::math::exp(c) : 0.999999
    std::exp(c.value()) : 0.999999
    

    I believe this is because the different calculations result in objects with different types:

    # const auto c = 1.0_um * (-1 / 1.0_m);
    (lldb) p c
    (const units::unit_t<units::unit<std::ratio<1, 1000000>, units::base_unit<std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1> >, std::ratio<0, 1>, std::ratio<0, 1> >, double, units::linear_scale>) $0 = {
      units::linear_scale<double> = (m_value = -1)
    }
    
    # const auto c = 1.0_um / 1.0_m;
    (lldb) p c
    (const units::dimensionless::scalar_t) $0 = {
      units::linear_scale<double> = (m_value = 9.9999999999999995E-7)
    }
    

    Where the latter is equivalent to:

    # const auto c = 1.0_um / 1.0_m;
    (lldb) p c
    (const units::unit_t<units::unit<std::ratio<1, 1>, units::base_unit<std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1> >, std::ratio<0, 1>, std::ratio<0, 1> >, double, units::linear_scale>) $0 = {
      units::linear_scale<double> = (m_value = 9.9999999999999995E-7)
    }
    

    The difference in the results amounts to the "new" calculation having the "correct" value directly in m_value, whereas the "old" calculation has some information in the type, which could be described as the exponent in the type and the mantissa in m_value. Floating-point errors aside, they are equal.

    This points to the ultimate cause of the issue: operator()() and value()/to<>() appear to consider different information when producing their results. operator()() fully discards information encoded in the type, directly returning the "raw" underlying value (modified for the decibel scale if needed), while for dimensionless types value() and to<>() account for both information encoded in the type and the "raw" underlying value in their return values. This is apparent in their implementations, where operator()() has no awareness of the units::unit tag, but value()/to<>() delegate to the conversion operator for dimensionless quantities, which explicitly "normalizes" the underlying value using units::convert<>() before returning it:

    template<class Units, typename T = UNIT_LIBDEFAULT_TYPE, template<typename> class NonLinearScale = linear_scale>
    class unit_t : public NonLinearScale<T>, units::detail::_unit_t
    {
    public:
        typedef T underlying_type;
    
        inline constexpr underlying_type value() const noexcept
        {
            return static_cast<underlying_type>(*this);
        }
    
        template<typename Ty, class = std::enable_if_t<std::is_arithmetic<Ty>::value>>
        inline constexpr Ty to() const noexcept
        {
            return static_cast<Ty>(*this);
        }
    
        template<class Ty, std::enable_if_t<traits::is_dimensionless_unit<Units>::value && std::is_arithmetic<Ty>::value, int> = 0>
        inline constexpr operator Ty() const noexcept
        {
            // this conversion also resolves any PI exponents, by converting from a non-zero PI ratio to a zero-pi ratio.
            return static_cast<Ty>(units::convert<Units, unit<std::ratio<1>, units::category::scalar_unit>>((*this)()));
        }
    };
    
    template<typename T>
    struct linear_scale
    {
        inline constexpr T operator()() const noexcept { return m_value; }
        T m_value;
    };
    
    template<typename T>
    struct decibel_scale
    {
        inline constexpr T operator()() const noexcept { return 10 * std::log10(m_value); }
        T m_value;
    };
    

    As a result, the problem is clear: the functions using operator()() to obtain a value to pass to std::math functions are incorrect and should be changed to use value() so relevant information is not thrown away.

    opened by ts826848 0
  • Fix UNITS_<VAR> def and <var> use mismatch

    Fix UNITS_ def and use mismatch

    A few variables prefixed with UNITS_ are defined to control various build options, but prefix-less versions are checked in the rest of the file when determining their values. This makes the options effectively useless.

    This commit unifies definition and use so the options actually affect the build.

    opened by ts826848 0
  • Question about underlying value access/casting

    Question about underlying value access/casting

    The README mentions unit_t::to<T> and unit_cast<T> for accessing the underlying value as T. However the implementation of these seem to be using static_cast<T> internally, and (due to an unit_t::operator T() I guess?) it works externally too.

    Is using static_cast for accessing the underlying value considered non-idiomatic? Are there any pitfalls I should be aware of when using static_cast?

    opened by yzsolt 0
  • Arguments of hyperbolic functions should not be angles.

    Arguments of hyperbolic functions should not be angles.

    I am using the units.h file as of commit ea6d126942cb3225a341568ab57ec52513977875. The issue described below can be reproduced using gcc-11.2.0 (and probably using any other C++14 compiler).

    The hyperbolic functions in units.h (such as units::math::cosh(), for example) require that their arguments be in units of angles. However, this does not appear to be correct. Unlike trigonometric functions, arguments of hyperbolic functions are dimensionless real numbers. Compare the behaviors of std::cos() and std::cosh(), for example.

    From Wikipedia:

    The hyperbolic functions take a real argument called a hyperbolic angle...

    In geometric applications, the argument of a trigonometric function is generally the measure of an angle. For this purpose, any angular unit is convenient, and angles are most commonly measured in conventional units of degrees in which a right angle is 90° and a complete turn is 360° (particularly in elementary mathematics).

    opened by nholthaus-units-user 0
  • Different results using std::exp() and units::math::exp().

    Different results using std::exp() and units::math::exp().

    I am using the units.h file as of commit ea6d126942cb3225a341568ab57ec52513977875. The issue described below can be reproduced using gcc-11.2.0 (and probably using any other C++14 compiler).

    Using std::exp() and units::math::exp() on the same value can produce confusingly different results and the reason for it is non-intuitive. Here is some code that illustrates this:

    #include "units.h"
    
    #include <iostream>
    #include <cmath>
    
    using namespace units::literals;
    
    int main()
    {
        const auto a = -1.0 / 1_m ;
        const auto b = 1_um ;
        const auto c = a * b ;
        std::cout << "c                   : " << c << std::endl ;
        std::cout << "units::math::exp(c) : " << units::math::exp(c) << std::endl ;
        std::cout << "std::exp(c.value()) : " << std::exp(c.value()) << std::endl ;
        return EXIT_SUCCESS ;
    }
    

    The output looks something like the following:

    c                   : -1e-06
    units::math::exp(c) : 0.367879
    std::exp(c.value()) : 0.999999
    

    The value of c appears to be -1e-06 here and, hence, std::exp(c.value()) appears to produce the expected result. However, units::math::exp(c) produces a different result.

    Is this intended/expected behavior?

    opened by nholthaus-units-user 2
Releases(3.0.0.alpha-3)
  • 3.0.0.alpha-3(Oct 28, 2020)

  • v3.0.0.alpha-2(Jun 22, 2018)

  • v3.0.0.alpha(Apr 26, 2018)

  • v2.3.1(Apr 22, 2018)

    New in v2.3.1

    This version removes support for the Visual Studio 2013 compiler.

    Features:

    • units now include constexpr name() and abbreviation() member functions, which do not really on string/iostream.
    • Builds with VS2017 Ninja generator out of the box
    • string conversions are now locale aware
    • added unary incremement and decremement operators (++,--), as well as unary + operator.

    Bug fixs:

    • fixed compilation error when iostream was disabled
    Source code(tar.gz)
    Source code(zip)
  • v2.3.0(Sep 16, 2017)

    features:

    • 5x compile time improvement on MSVC.
    • 1.5x compile time improvement on GCC.
    • Even more dramatic reductions in compile time can be acheived if you opt-in to specific unit definitions instead of using all the library-defined types (which is the default value). Check out Enabling a subset of units to improve compilation time for instructions.
    • Adds std::cout support for units with no defined abbreviation (they show up as a combination of SI base units)
    • Support for std::numeric_limits of unit types.
    • Assignment operators for unit types: -=, +=, /=, *=.
    • Added min and max overloads for units types in units::math.
    • Added to_string function and abbreviation functions:
      auto len = 3.5_m;
      auto str = units::length::to_string(len);
      auto abv = units::length::abbreviation(len);
      
      std::cout << str;  // prints "3.5 m"
      std::cout << abv;  // prints "m"
      
    • Added units of data and data transfer: bits, bytes, bits_per_second, and bytes_per_second.
    • Adds value() member for accessing underlying type.
    • Adds value_type trait, as a synonym for underlying_type.
    • Adds definitions for Julian and Gregorian years.
    • Thanks to @dinocore1, units now supports cmake install and find_packages. From the pull request:
      # To have cmake install units library to a local 'install' directory:
      mkdir build
      cd build
      cmake -DCMAKE_INSTALL_PREFIX="install" ..
      cmake --build . --target install
      # The units library can then be used in some other cmake project using 
      # the standard 'find_package' command. Like so:
      find_package(units)
      

    Bug fixes:

    • Fixed singualr name of siemen to be siemens (Thanks @Oxyd)
    • Fixed bug with cubrt operation (Thanks @PearCoding)
    • fixed constexpr relational operators bug
    • fixed exponential temperature conversions (Thanks @guarndt)
    Source code(tar.gz)
    Source code(zip)
  • v2.3.0RC2(Feb 23, 2017)

    Improvements:

    • 512% compile time improvement on MSVC.
    • 165% compile time improvement on GCC.

    Bug fixes:

    • fixed constexpr relational operators bug (#63)

    Notes:

    • Retains VS2013 support
    • No documentation updates are provided in this release candidate.
    Source code(tar.gz)
    Source code(zip)
  • v2.3.0RC1(Feb 13, 2017)

    Release Candidate 1 for v2.3.0

    • Adds data units (bits/bytes) and data transfer units as first-class citizens
    • Adds std::cout support for units with no defined abbreviation (they show up as a combination of SI base units)
    • Adds compound assignment operators: +=, -=, /=, *=.
    • Adds value() member for accessing underlying type.
    • Adds value_type trait, as a synonym for underlying_type.
    • Adds definitions for Julian and Gregorian years.

    Notes:

    • Provisionally removes compiler support for MSVC2013.
    • No documentation updates are included in this candidate.
    Source code(tar.gz)
    Source code(zip)
  • v2.2.0(Feb 9, 2017)

    New Features:

    • Unit types and member functions are now constexpr and noexcept wherever possible.
    • Added cpow<..>(), a constexpr power function.
    • Added make_unit factory function.
    • Added cmath hypot() function.
    • Added optional definition to disable IOStream in embedded applications.

    Improvements:

    • New and improved pi constant
    • units::time and std::chrono are now interoperable
    • eliminated warnings on gcc when using -Wall -Wextra -pedantic
    Source code(tar.gz)
    Source code(zip)
  • v2.1.3(Oct 9, 2016)

    New features:

    • Added a CMake interface project so that units can be more easily included as a git submodule or CMake subdirectory.
    • Added options to disable building unit tests and documentation.

    Improvements:

    • Removed all GNU dependencies
    • Updated google test to version 1.8.0 and simplified the build process
    • improved overall units compile time.
    Source code(tar.gz)
    Source code(zip)
  • v2.1.2(Sep 29, 2016)

  • v2.1.1(Sep 28, 2016)

  • v2.1.0(Sep 25, 2016)

    New features:

    • Literal suffixes for instantiating unit containers (c++14 compliant compiler required).

      auto area = 3.0_m * 4.0_m;    // area == square_meter_t(12.0) ==   12_sq_m;
      
    • std::cout output now includes the unit abbreviations.

      mile_t distance(26.2);
      std::cout << distance;    // printed: 26.2 mi
      
    • Unit-to-built-in-type conversions using to<> or unit_cast.

      mile_t distance(26.2);
      double result = unit_cast<double>(distance); // result == 26.2
      
    • Unit definition macros.

      UNIT_ADD(length, foot, feet, ft, unit<std::ratio<381, 1250>, meters>)
      
    • Improvements for integral unit types.

    Notes:

    • Due to incompatibilites with the WINAPI, the literal abbreviation for tesla units are _Te, instead of the SI standard _T.

    Tested on:

    • gcc-4.9.3
    • gcc-5.4.0
    • msvc2013
    • msvc2015
    Source code(tar.gz)
    Source code(zip)
  • v2.0.3(Apr 14, 2016)

  • v2.0.2(Apr 14, 2016)

  • v2.0.1(Apr 6, 2016)

  • v2.0.0(Apr 4, 2016)

    New features:

    Compile-time unit arithmetic via unit_value_t
    Unit-enabled ports of most <cmath> functions, including c++11 extensions.
    Square-root manipulators for unit, unit_t, and unit_value_t
    Improved documentation
    

    Tested on:

    gcc -4.9
    msvc2015
    
    Source code(tar.gz)
    Source code(zip)
  • API_CHANGE(Mar 29, 2016)

  • v1.3.0(Mar 10, 2016)

  • v1.2.2(Feb 28, 2016)

  • v1.2.1(Feb 27, 2016)

    bug fixes and namespace cleanup.

    • Corrected issues where const and const & unit_t types would fail their respective is_[category]_unit tests.
    • Moved implementation classes to details namespace.
    Source code(tar.gz)
    Source code(zip)
  • v1.2.0(Feb 12, 2016)

  • v1.1.1(Feb 11, 2016)

  • v1.1.0(Feb 11, 2016)

Owner
Nic Holthaus
C++ Software engineer
Nic Holthaus
A fast character conversion and transliteration library based on the scheme defined for Japan National Tax Agency (国税庁) 's corporate number (法人番号) system.

jntajis-python Documentation: https://jntajis-python.readthedocs.io/ What's JNTAJIS-python? JNTAJIS-python is a transliteration library, specifically

Open Collector, Inc. 12 May 16, 2022
provide SFML Time utilities in pure C++20, no dependencies

SFML-Time-utilities-without-SFML provide SFML Time utilities in pure C++20, no dependencies Example int main() { Clock clock; Sleep(1000);

null 1 Apr 28, 2022
Context Free Grammars to Pushdown Automata Conversion, C++ program

CFG-to-PDA-Conversion Context Free Grammars to Pushdown Automata Conversion, C++ program USF Group Project: Was in charge of Lambda Removal, Unit Remo

Matias 0 Mar 15, 2022
cavi is an open-source library that aims to provide performant utilities for closed hierarchies (i.e. all class types of the hierarchy are known at compile time).

cavi cavi is an open-source library that aims to provide performant utilities for closed hierarchies (i.e. all class types of the hierarchy are known

Baber Nawaz 5 Mar 9, 2022
libcurses and dependencies taken from netbsd and brought into a portable shape (at least to musl or glibc)

netbsd-libcurses portable edition this is a port of netbsd's curses library for usage on Linux systems (tested and developed on sabotage linux, based

null 119 Jun 19, 2022
Small header-only C++ library that helps to initialize Vulkan instance and device object

Vulkan Extensions & Features Help, or VkExtensionsFeaturesHelp, is a small, header-only, C++ library for developers who use Vulkan API.

Adam Sawicki 10 Apr 13, 2022
A header only C++ library that provides type safety and user defined literals for physical units

SI - Type safety for physical units A header only c++ library that provides type safety and user defined literals for handling pyhsical values defined

Dominik Berner 379 Jul 21, 2022
Compile and execute C "scripts" in one go!

c "There isn't much that's special about C. That's one of the reasons why it's fast." I love C for its raw speed (although it does have its drawbacks)

Ryan Jacobs 2k Aug 7, 2022
match(it): A lightweight header-only pattern-matching library for C++17 with macro-free APIs.

match(it): A lightweight header-only pattern-matching library for C++17 with macro-free APIs. Features Easy to get started. Single header library. Mac

Bowen Fu 229 Jul 30, 2022
C++ header-only library for generic data validation.

C++ header-only library for generic data validation.

Evgeny Sidorov 26 Jul 13, 2022
Small Header only library to parse argv for flags

Small Header only library to parse argv for flags

Ben Wernicke 2 Dec 8, 2021
Header only roguelike rendering library.

Header only roguelike rendering library. Support for Opengl33 and Raylib. Features Support for custom glyph atlasses with up to 65655 tiles of custom

Journeyman 6 Aug 1, 2022
Example library and blog that explain how JSI modules are built from scratch in React Native

react-native-simple-jsi This is an example library that explains how anyone can build jsi modules from scratch in React Native. This code is written a

Ammar Ahmed 115 Jul 29, 2022
A Header-Only Engine that tries to use SFML in a deeper level

⚙️ SFML-Low-Level-Engine ⚙️ A header-only library that tries to use SFML at a deeper level ?? Instalation Download the source code and put the GLD fol

!Gustavo! 4 Aug 27, 2021
Header-only C++20 wrapper for MPI 4.0.

MPI Modern C++20 message passing interface wrapper. Examples Initialization: mpi::environment environment; const auto& communicator = mpi::world_c

Ali Can Demiralp 29 Apr 8, 2022
Header-only lock-free synchronization utilities (one writer, many readers).

stupid Header-only lock-free synchronization utilities (one writer, many readers). No queues Base functionality The base functionality of this library

Colugo 13 Jun 9, 2022
This is a Header-only c++ string implentation which specializes in dealing with small strings. 🧵

string-impl This is a Header-only c++ string implentation which specializes in dealing with small strings. ?? Usage ⌨ Instantiation A string can be in

null 1 Apr 1, 2022
Anime browser built with AniList APIs, showcasing clean Flutter BLoC architecture

Anime browser built with AniList APIs. The purpose of this project is to showcase clean Flutter application architecture with BLoC design pattern.

Peter A. Bizjak 21 Jun 22, 2022
Handling C++ & __try exceptions without the need of built-in handlers.

manual_exception_handling this handles exceptions inside the module its specified to manage without needing previous handlers for the exception a good

null 36 Aug 8, 2022