nholthaus / units Goto Github PK
View Code? Open in Web Editor NEWa 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
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
In line 910 in file include/units.h. (master branch)
The function std::ratio_multiply has to be replaced by std::ratio_divide. If not, cbrt_base_impl is just the same as cubed_base_impl and does not represent a cube rooted unit.
I think this is just a typo :)
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.
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.
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?
fix build issues with clang-3.8
and add testing to travis.
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.
at least:
<=
>=
==
(for floating point types)Add some unit tests!
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.
Use user defined suffixes of the form
kilometer_t operator""_km(long double val){ return kilometer_t(val); }
It's probably worth creating a macro to generate these from the abbreviated type definitions.
#undef
for macros (WinAPI/ARM)make_unit
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.
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.
bool value = units::is_length_unit<const meter_t>::value;
EXPECT_TRUE(value); // fails, should succeed
bool value = units::is_length_unit<const meter_t&>::value;
EXPECT_TRUE(value); // fails, should succeed
Is there a way to handle percentages vis-à-vis decimal representation (5% vs. 0.05)? What would it take to implement that?
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?
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.
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.
std::data
?if constexpr(...)
)?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)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.
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?
TODO:
#defines
and appveyor references to VS2013. Refactor typename std::... ::type
code that was reintroduced for VS2013.RC 1 changes:
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.
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.
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.
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.
bit, byte, etc. Add a new data
category to struct base_unit
, as well as a data_throughput
series of units.
Julian/Gregorian at minimum.
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;
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.
I'm testing units.h
in a small project and get the following compile times using clang:
units.h
UNIT_LIB_DISABLE_IOSTREAM
macroWhile 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.
Wrap the hypot
function from the <cmath>
library.
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?
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);
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.
It would be nice to have a full set of compound assignment operations available, such as operator +=,
operator -= etcetera.
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:
Operator += has the same issue.
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.
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.
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;
Visual Studio compile times for the units library are currently under investigation as a bug by Microsoft.
Please vote on this if it is affecting your compile times!
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.
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?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.