GithubHelp home page GithubHelp logo

nholthaus / units Goto Github PK

View Code? Open in Web Editor NEW
936.0 35.0 134.0 14.48 MB

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

Home Page: http://nholthaus.github.io/units/

License: MIT License

CMake 3.66% C++ 96.34%
unit-conversion dimensional-analysis cpp14 template-metaprogramming compile-time header-only no-dependencies

units's People

Contributors

alexdewar avatar augustoicaro avatar bfueldner avatar crisluengo avatar cstratopoulos avatar delgan avatar dharmatech avatar erroneous1 avatar globberwops avatar jml1795 avatar johelegp avatar maikel avatar martinmoene avatar morwenn avatar nacorpio avatar nholthaus avatar opportunityliu avatar oxyd avatar pvaibhav avatar samwot avatar sciencewhiz avatar tarberd avatar ts826848 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

units's Issues

Electric conductance singular unit wrong: siemen instead of siemens

The SI unit of electric conductance is the siemens – https://en.wikipedia.org/wiki/Siemens_(unit) . That is, “siemens” is both the singular and the plural. The library only has siemen_t, though.

This should be easy to fix, but there is a decision to be made about backward compatibility: Is it okay to just rename the unit, or do you want to leave the incorrect name for compatibility? Siemens is probably not a very widely used unit, so I think just renaming siemen_t to siemens_t should be okay.

Math functions and ADL

Why did you decide to put the math functions in a math:: subnamespace?

I really think that compile-time unit types are extremely useful tools to have at hand (standardization would be great), but people are more likely to migrate their code if changing their unitless types by unit types works out-of-the-box. I often develop generic algorithms, including generic mathematical algorithms where math functions are found via ADL in the implementation:

using std::abs;
auto res = abs(value);

Such algorithms could work out-of-the-box with suitable units types (when the operations make sense) had the mathematical functions been in the same namespace as the unit types, but your documentation says that such calls would be ambiguous.

Do you think that ADL-found mathematical functions for your unit types would be a solvable problem?

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.

create `unit_cast` function

The purpose of a unit cast would be similar to const_cast: remove the type safety. It would also be able to convert to other built-in types than just the underlying_type.

This is the exact same functionality the to member function currently exhibits, but I think the syntax will be clearer or more palatable to some people.

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.

Why std::ratio?

Sorry, if this was already explained somewhere, but why does this library use std::ratio instead of simple integers for the unit exponents? Are there usecases, where you need a non-integer exponent?

unit_t as constexpr

I am trying to use the sample code as a constexpr

#include "units/include/units.h"

constexpr auto Lx = units::length::meter_t(5);

update: area -> Lx

so I can use those in constexpr functions (C++11/14).

Unfortunately, that throws with gcc 4.9.8 (-std=c++11):

error: call to non-constexpr function
‘units::unit_t<
  Units, T, NonLinearScale
>::unit_t(T, const Args& ...) [with Args = {};
Units = units::unit<
  std::ratio<1l>,
  units::base_unit<std::ratio<1l> >
>;
T = double; NonLinearScale = units::linear_scale]’
 constexpr auto area = units::length::meter_t(5);

Missing operator -= and operator +=

Testcase:

using namespace units;
using namespace units::voltage;
using namespace units::literals;

volt_t v = 4_V;
v -= 2_V;

Results in

foo.cpp:10:5: error: no match for ‘operator-=’ (operand types are ‘units::voltage::volt_t {aka units::unit_t<units::unit<std::ratio<1l>, units::base_unit<std::ratio<2l>, std::ratio<1l>, std::ratio<-3l>, std::ratio<0l>, std::ratio<-1l> > > >}’ and ‘units::voltage::volt_t {aka units::unit_t<units::unit<std::ratio<1l>, units::base_unit<std::ratio<2l>, std::ratio<1l>, std::ratio<-3l>, std::ratio<0l>, std::ratio<-1l> > > >}’)
   v -= 2_V;
   ~~^~~~~~

And indeed, I can't seem to find a single mention of operator -= in units.h anywhere. The documentation does mention it, though:

template<class Units , typename T , template< typename > class NonLinearScale, typename RhsType >
unit_t< Units, T, NonLinearScale > & 	operator-= (unit_t< Units, T, NonLinearScale > &lhs, const RhsType &rhs) noexcept

So there's two issues here:

  1. The documentation contains things that don't exist
  2. The operator doesn't exist, which means that one has to do v = v - 2_V; This is somewhat suboptimal.

Operator += has the same issue.

Confirmed working on clang 3.7

This might be as expected, but I built it on clang-3.7.1 just for kicks.

mkdir build
cd build
cmake ..
./unitTests/unitLibTest

all tests pass and was successfully compiled.

Compound assignments?

Is there any particular reason for the lack of compound assignment support in this library? At a glance it looks like it would only be a few lines to implement the new operators (which I would be happy to contribute if necessary).

Currently I have many statements like this littering my code:
joint.momentum = joint.momentum + dt * force;

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.

minimal functionality and compilation speed

I'm testing units.h in a small project and get the following compile times using clang:

  • ~1 sec before including units.h
  • 11 secs after header inclusion
  • 3 secs with UNIT_LIB_DISABLE_IOSTREAM macro

While all modest compilation times, it disturbs me to increase compilation time by an order of magnitude (I like to have cout functionality), especially since I use only a fraction of the defined units. It would be great to have a way of including minimal functionality and add the needed units manually using the UNIT_ADD macro.

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.

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.

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.

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.

Update readme for v2.2.0

  • "What's new"
  • New CMake options
  • new unit macros
  • how to use as cmake subfolder
  • units for embedded
  • #undef for macros (WinAPI/ARM)
  • make_unit
  • Special thanks

Typedef notation for underlying number types

Hello!

As an option for the library, it would be quite useful to generate typedefs for multiple number formats in one project. Currently the library only defines _t-suffixed typedefs with an underlying type defined by UNIT_LIB_DEFAULT_TYPE. I propose supporting common or possibly user-defined number types with other type suffixes. Macro-generated names might look like this:

using meter_i = unit_t<meter, int>;
using meter_f = unit_t<meter, float>;
using meter_d = unit_t<meter, double>;

As an example use-case, I might represent (absolute) points in a simulated world using fixed-point values at the millimeter scale, and vectors using floating-point values at the meter scale. I may have certain numerically-unstable math here and there which I wish to promote to double-precision, in a project otherwise using single precision.

I suspect this should be optional, and disabled by default, to avoid code bloat.

specifying units for the ISO standard

Hi
I am trying to start specification of a std::units library and just crossed your implementation. I would like to discuss your design goals and, for example, learn why you rely on std::ratio and provide dimensions for "dimensionless" things like radians. (I found you because of your questions why there is no standard conversion from std::ratio values/types to floating points, I believe that is an oversight that is now doable with variable templates at compile time, when it wasn't when ratio was first implemented and specified).

So far, I took a look at boost::units which is unusable because of its compile errors on misuse and also because it is quite oldish C++, my current option to start with is PhysUnit-CT-Cpp11 that I am adopting to C++14. You can reach me at [email protected]

Thanks
Peter.

Release v2.3.0

TODO:

  • Document changes
  • Release candidate feedback
  • (maybe?) Remove #defines and appveyor references to VS2013. Refactor typename std::... ::type code that was reintroduced for VS2013.

RC 1 changes:

  • #29 Add other definitions for year
  • #38 cout << 2.0_m * 2.0_m * 2.0_m * 2.0_m << endl;
  • #44 Compound assignments
  • #46 Add data size units as first-class citizens
  • #54 pascal defined elsewhere
  • #56 value() and value_type

If interested, please try out the release candidate. Feedback is welcome, including for additional features!

The goal is to provide a production release in early March.

Compiler error when UNIT_LIB_DEFAULT_TYPE is changed to float

When I changed the UNIT_LIB_DEFAULT_TYPE to float, my compiler (gcc-arm) spits out a very long error basically boiling down to:

units/include/units.h:1452:28: error: '(6.022141e+23f * 6.022141e+23f)' is not a constant expression
    return y == 0 ? 1.0 : x * pow(x, y - 1);
                          ~~^~~~~~~~~~~~~~~

It took a long time to realize the reason: (6.022141e+23f * 6.022141e+23f) is of the order of 10^46, which doesn't fit in a single-recision float whose max value is around 10^38.

A little more debugging later turns out the derivation of Stefan-Boltzmann constant around line 3954:

static constexpr const unit_t<compound_unit<power::watts, inverse<area::square_meters>, inverse<squared<squared<temperature::kelvin>>>>>
	sigma((2 * math::cpow<5>(pi) * math::cpow<4>(R)) / (15 * math::cpow<3>(h) * math::cpow<2>(c) * math::cpow<4>(N_A)));	///< Stefan-Boltzmann constant.

contains Avogadro's number raised to 4th power.

I replaced this with pre-calculated constant, and the error disappeared:

static constexpr const unit_t<compound_unit<power::watts, inverse<area::square_meters>, inverse<squared<squared<temperature::kelvin>>>>>
	sigma(5.670367e-8);///< Stefan-Boltzmann constant.

Would anyone miss Visual Studio 2013 support if it was gone?

My original intention was to support Visual Studio 2013 through the v2.x.x series of releases. However, the library is becoming increasingly difficult to maintain because of the hoops and basically occult practices required to avoid dreaded internal compiler errors. In addition, I'm not sure that the test cases adequately reflect whether an ICE error will occur in user code.

VS2013 was originally supported because of a lack of CUDA/Qt support for VS2015, which has long since been remedied. It won't continue to be possible for long as support for C++17 features are rolled in.

So, I'm wondering if VS2013 support was discontinued in the v2.x.x series of releases (i.e. in version 2.3.0) would anyone actually care?

Unexpected output from units::concentration::percent_t::to<...>()

Consider the following program:

units::concentration::percent_t x(100.0);
double y = x.to<double>();
std::cout << x << std::endl;
std::cout << y << std::endl;

Output:

100 pct
1

I would have expected the scale of the output of percent_t::to<>() to match the scale of the constructor parameter. However, a value of 100 goes in, but a value of 1 comes out. Is this behavior as intended?

Compound assignment operators

It would be nice to have a full set of compound assignment operations available, such as operator +=,
operator -= etcetera.

pascal defined elsewhere

I just ran into the issue (on VS2015) that the header minwindef.h (that is pulled in from somewhere, not sure where) defines this:

#if (!defined(_MAC)) && ((_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED))
#define pascal __stdcall
#else
#define pascal
#endif

which obviously conflicts with defining pascal as a unit.

iostream support blows up binary size and RAM requirements

Currently iostream support is unconditionally enabled. This is fine for desktop targets but not for small embedded targets with limited flash storage and RAM. For instance on the Nordic nRF51 based on ARM Cortex-M0+, approx 150kB flash and 6.5kB of extra RAM is used, on a device which only has 256kB flash and 8kB RAM.

Disabling iostream support will go a long way in supporting embedded targets.

The following patch (against v2.2.x branch) adds a #define to enable/disable iostream:
https://gist.github.com/pvaibhav/c3703bf23c8513f4a8160eed524a415d

I've verified disabling iostream results in zero impact on the compiled binary size or RAM usage.

I'll create a pull request from this patch if there is any interest. My only concern is the duplication of UNIT_ADD definition, once with and once without iostream. An intermediate macro could perhaps help here.

Conversion between Celsius and Kelvin seems to fail

Hi Nic, I have recently added your library to my project to convert a Platinum sensor's resistance to its temperature. I like the library especially because the std::math functions have been ported, but there is one issue I stumbled across yesterday when performing the mentioned conversion. It required the square root of a value given in °C² to be calculated. That should give a result in °C, but seems to convert to Kelvin in between, and not to convert back correctly.
The following code reproduces the problem:
using squared_celsius = units::compound_unit<squared<celsius>>;
using squared_celsius_t = units::unit_t<squared_celsius>;
const squared_celsius_t right(100);
const celsius_t rootRight = units::math::sqrt(right);
std::cerr << rootRight << std::endl;
It prints "-263.15 degC", which is 10 K, but in my opinion should print "10 degC".
I use a SysGCC 4.9.2 compiler from Windows to Raspberry Pi Linux.

Add constexpr/noexcept wherever possible

The original library was targeted for VS2013, so constexpr/noexcept were not really high on the list of priorities.

However, with the renewed focus on other compilers, and the Microsoft/GSL examples of how to macro-ize those keywords for VS2013, I think it's time to address this.

Commit e26fcd7 causes VS 2017 compiler to crash

I get a fatal error C1001: An internal error has occurred in the compiler.
1>(compiler file 'msc1.cpp', line 1469)

A minimal example that causes this to occur:

void test(void)
{
auto y1 = units::length::meter_t(1) * units::length::meter_t(1);
auto y2 = units::length::meter_t(1) + units::length::meter_t(1); // VS 2017 chokes on this line
}

This might well be a VC++ issue - would anyone have a clue on what is going on here and how the latest commit would impact what happens under the hood?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.