Skip to content

Commit

Permalink
Unit data operators (#12)
Browse files Browse the repository at this point in the history
* Change unit data operators so they match what they doing instead of assuming they are power based.  +-> * and - to /  + and - are only appropriate if the assumption of powers holds true, but it doesn't abstract it very much.  * and / are just clearer in my opinion.

* move root operation to a free function for all but unit_data.  So the HEADER_ONLY classes are the same as the non-header only classes and the there will be no class conflicts in object files.

* change pow on measurements to be a free function

* fix formatting in units.cpp

* update README.md to match changes to measurement and unit free functions
  • Loading branch information
phlptp authored Jan 15, 2020
1 parent baa6141 commit 4227623
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 167 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ These operations apply to units and precise_units
- also available are copy constructor and copy assignments
- `<unit> inv()` generate a new unit containing the inverse unit `m.inv()= 1/m`
- `<unit> pow(int power)` take a unit to power(NOTE: beware of limits on power representations of some units, things will always wrap so it is defined but may not produce what you expect). `power` can be negative.
- `<unit> root(int power)` non constexpr, take the root of a unit, produces `error` unit if the root is not well defined. power can be negative.
- `bool is_exactly_the_same(<unit>)` compare two units and check for exact equivalence in both the unit_data and the multiplier, NOTE: this uses double equality
- `bool has_same_base(<unit>|<unit_data>)` check if the <unit_data> is the same
- `equivalent_non_counting(<unit>|<unit_data>)` check if the units are equivalent ignoring the counting bases
Expand Down Expand Up @@ -204,6 +203,7 @@ These functions are not class methods but operate on units
- `bool is_valid(<unit>)` check to make sure the unit is not an invalid unit( the multiplier is not a NaN) and the unit_data does not match the defined `invalid_unit`.
- ` bool is_temperature(<unit>)` return true if the unit is a temperature unit such as `F` or `C` or one of the other temperature units.
- `bool is_normal(<unit>)` return true if the multiplier is a normal number, there is some defined unit base, not the identity unit, the multiplier is not negative, and not the default unit. basically a simple way to check if you have some non-special unit that will behave more or less how you expect it to.
- `<unit> root(<unit>, int power)` non constexpr, take the root of a unit, produces `error` unit if the root is not well defined. power can be negative.
- `<unit> sqrt(<unit>)` convenience function for taking the sqrt of a unit.

### Measurement Operations
Expand All @@ -214,8 +214,6 @@ These functions are not class methods but operate on units
- `<unit> units() const` get the units used as a basis for the measurement
- `<unit> as_unit() const` take the measurement as is and convert it into a single unit. For Examples say a was 10 m. calling as_unit() on that measurement would produce a unit with a multiplier of 10 and a base of meters.
- `double value_as(<unit>)` get the value of a measurement as if it were measured in \<unit>
- `<measurement> root(int)` generate a root of a measurement
- `<measurement> pow(int)` generate a measurement which is a specific power of another measurement

#### Uncertain measurement methods
Uncertatin measurements have a few additional functions to support the uncertainty calculations
Expand All @@ -239,10 +237,12 @@ Notes: for regular measurements, `+` and `-` are not defined for doubles due to
- `<measurement>=<unit>/<double>`
- `<measurement>=<double>/<unit>` basically calling a number multiplied or divided by a <unit> produces a measurement, `unit` produces a measurement and `precise_unit` produces a precise_measurement.

#### Measurement function
#### Measurement functions

- `measurement measurement_cast(<measurement>)` convert a precise_measurement into measurement
- `fixed_measurement measurement_cast(fixed_precise_measurement)` convert a fixed_precise_measurement into a fixed_measurement
- `fixed_measurement measurement_cast(<fixed*_measurement>)` convert a fixed_precise_measurement or fixed_measurement into a fixed_measurement
- `<measurement> pow(<measurement>, int)` generate a measurement which is a specific power of another measurement
- `<measurement> root(<measurement>, int)` generate a root of a measurement
- `<measurement> sqrt(<measurement>)` take the square root of a measurement of any kind, the units need to have a valid root.

### Available library functions
Expand All @@ -269,7 +269,7 @@ The units library has some support for commodities, more might be added in the
- `enableUserDefinedCommodities()` enable the use of UserDefinedCommodities. they are enabled by default.

#### Other unit definitions
These are all only partially implemented
These are all only partially implemented, not recommended for use yet
- `precise_unit x12_unit(string)` get a unit from an X12 string.
- `precise_unit dod_unit(string)` get a unit from a DOD code string.
- `precise_unit r20_unit(string)` get a unit from an r20 code string.
Expand Down
18 changes: 9 additions & 9 deletions test/test_measurement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,14 @@ TEST(measurement, powroot)
{
measurement m1(2.0, m);

auto v1 = m1.pow(3);
auto v1 = pow(m1, 3);
EXPECT_EQ(v1.value(), 8.0);
EXPECT_EQ(v1.units(), m.pow(3));

auto m2 = v1.root(3);
auto m2 = root(v1, 3);
EXPECT_TRUE(m2 == m1);

auto m0 = v1.root(0);
auto m0 = root(v1, 0);
EXPECT_EQ(m0.value(), 1.0);
EXPECT_EQ(m0.units(), one);

Expand Down Expand Up @@ -343,11 +343,11 @@ TEST(fixedMeasurement, powroot)
{
fixed_measurement m1(2.0, m);

auto v1 = m1.pow(3);
auto v1 = pow(m1, 3);
EXPECT_EQ(v1.value(), 8.0);
EXPECT_EQ(v1.units(), m.pow(3));

auto m2 = v1.root(3);
auto m2 = root(v1, 3);
EXPECT_TRUE(m2 == m1);

fixed_measurement m4(16.0, m.pow(2));
Expand Down Expand Up @@ -470,11 +470,11 @@ TEST(PreciseMeasurement, powroot)
{
precise_measurement m1(2.0, precise::m);

auto v1 = m1.pow(3);
auto v1 = pow(m1, 3);
EXPECT_EQ(v1.value(), 8.0);
EXPECT_EQ(v1.units(), precise::m.pow(3));

auto m2 = v1.root(3);
auto m2 = root(v1, 3);
EXPECT_TRUE(m2 == m1);
precise_measurement m4(16.0, precise::m.pow(2));
EXPECT_EQ(sqrt(m4), precise_measurement(4.0, precise::m));
Expand Down Expand Up @@ -517,11 +517,11 @@ TEST(fixedPreciseMeasurement, powroot)
{
fixed_precise_measurement m1(2.0, precise::m);

auto v1 = m1.pow(3);
auto v1 = pow(m1, 3);
EXPECT_EQ(v1.value(), 8.0);
EXPECT_EQ(v1.units(), precise::m.pow(3));

auto m2 = v1.root(3);
auto m2 = root(v1, 3);
EXPECT_TRUE(m2 == m1);

fixed_precise_measurement m4(16.0, precise::m.pow(2));
Expand Down
10 changes: 5 additions & 5 deletions test/test_uncertain_measurements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,15 +254,15 @@ TEST(uncertainOps, pow1)
uncertain_measurement y(3.0, 0.6, cm);
uncertain_measurement Av(2.0, 0.2, cm.pow(2));

auto z = w * y.pow(2) / Av.root(2);
auto z = w * pow(y, 2) / root(Av, 2);
EXPECT_NEAR(z.value(), 28.765, 0.0005);
EXPECT_NEAR(z.uncertainty(), 13.07, 0.005);

auto z2 = w * y.pow(2) / sqrt(Av);
auto z2 = w * pow(y, 2) / sqrt(Av);
EXPECT_NEAR(z2.value(), 28.765, 0.0005);
EXPECT_NEAR(z2.uncertainty(), 13.07, 0.005);

auto zs = w.rss_product(y.pow(2)).rss_divide(Av.root(2));
auto zs = w.rss_product(pow(y, 2)).rss_divide(root(Av, 2));
EXPECT_NEAR(zs.value(), 29, 0.5);
EXPECT_NEAR(zs.uncertainty(), 12, 0.5);
}
Expand Down Expand Up @@ -382,9 +382,9 @@ TEST(uncertainOps, testHeight)
uncertain_measurement t(0.6, 0.06, s);
measurement gc = 9.8 * m / s.pow(2);

auto y = v0 * t - 0.5 * gc * t.pow(2);
auto y = v0 * t - 0.5 * gc * pow(t, 2);

auto ys = v0.rss_product(t).rss_subtract(0.5 * gc * t.pow(2));
auto ys = v0.rss_product(t).rss_subtract(0.5 * gc * pow(t, 2));

EXPECT_NEAR(y.uncertainty(), 0.712, 0.005);
EXPECT_NEAR(y.value(), 0.636, 0.0005);
Expand Down
80 changes: 40 additions & 40 deletions test/test_unit_ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ TEST(unitOps, power)
{
auto m2 = m.pow(2);
EXPECT_EQ(m * m, m2);
auto m4 = m.pow(4);
auto m4 = pow(m, 4); //use the free function form
EXPECT_EQ(m * m * m * m, m4);
auto m4_b = m2.pow(2);
EXPECT_EQ(m4_b, m * m * m * m);
Expand All @@ -63,39 +63,39 @@ TEST(unitOps, power)

TEST(unitOps, root)
{
EXPECT_EQ(m.root(0), one);
EXPECT_EQ(root(m, 0), one);
auto m1 = m.pow(1);
EXPECT_EQ(m, m1.root(1));
EXPECT_EQ(m.inv(), m1.root(-1));
auto m2 = m.pow(2);
EXPECT_EQ(m, m2.root(2));
EXPECT_EQ(m, root(m1, 1));
EXPECT_EQ(m.inv(), root(m1, -1));
auto m2 = pow(m, 2);
EXPECT_EQ(m, root(m2, 2));

EXPECT_EQ(m, sqrt(m2));

auto m4 = m.pow(4);
EXPECT_EQ(m * m, m4.root(2));
EXPECT_EQ(m, m4.root(4));
EXPECT_EQ(m * m, root(m4, 2));
EXPECT_EQ(m, root(m4, 4));

auto ft1 = ft.pow(1);
EXPECT_EQ(ft, ft1.root(1));
EXPECT_EQ(ft.inv(), ft1.root(-1));
EXPECT_EQ(ft, root(ft1, 1));
EXPECT_EQ(ft.inv(), root(ft1, -1));

auto ft2 = ft.pow(2);
EXPECT_EQ(ft, ft2.root(2));
EXPECT_EQ(ft.inv(), ft2.root(-2));
EXPECT_EQ(ft, root(ft2, 2));
EXPECT_EQ(ft.inv(), root(ft2, -2));
auto ft3 = ft.pow(3);
EXPECT_EQ(ft, ft3.root(3));
EXPECT_EQ(ft.inv(), ft3.root(-3));
EXPECT_EQ(ft, root(ft3, 3));
EXPECT_EQ(ft.inv(), root(ft3, -3));
auto ft4 = ft.pow(4);
EXPECT_EQ(ft * ft, ft4.root(2));
EXPECT_EQ(ft, ft4.root(4));
EXPECT_EQ(ft.inv(), ft4.root(-4));
EXPECT_EQ(ft * ft, root(ft4, 2));
EXPECT_EQ(ft, root(ft4, 4));
EXPECT_EQ(ft.inv(), root(ft4, -4));

auto ft5 = ft.pow(5);
EXPECT_EQ(ft, ft5.root(5));
EXPECT_EQ(ft.inv(), ft5.root(-5));
EXPECT_EQ(ft, root(ft5, 5));
EXPECT_EQ(ft.inv(), root(ft5, -5));

EXPECT_EQ(unit(-4.5, m).root(2), error);
EXPECT_EQ(root(unit(-4.5, m), 2), error);
}

TEST(unitOps, nan)
Expand Down Expand Up @@ -314,7 +314,7 @@ TEST(preciseUnitOps, Power)
{
auto m2 = precise::m.pow(2);
EXPECT_EQ(precise::m * precise::m, m2);
auto m4 = m.pow(4);
auto m4 = pow(m, 4);
EXPECT_EQ(precise::m * precise::m * precise::m * precise::m, m4);
auto m4_b = m2.pow(2);
EXPECT_EQ(m4_b, precise::m * precise::m * precise::m * precise::m);
Expand All @@ -324,36 +324,36 @@ TEST(preciseUnitOps, Power)
TEST(preciseUnitOps, root)
{
auto m1 = precise::m.pow(1);
EXPECT_EQ(precise::m, m1.root(1));
EXPECT_EQ(precise::m.inv(), m1.root(-1));
auto m2 = precise::m.pow(2);
EXPECT_EQ(precise::m, m2.root(2));
EXPECT_EQ(precise::m, root(m1, 1));
EXPECT_EQ(precise::m.inv(), root(m1, -1));
auto m2 = pow(precise::m, 2); //use the alternate free function form
EXPECT_EQ(precise::m, root(m2, 2));
EXPECT_EQ(precise::m, sqrt(m2));
auto m4 = precise::m.pow(4);
EXPECT_EQ(precise::m * precise::m, m4.root(2));
EXPECT_EQ(precise::m, m4.root(4));
EXPECT_EQ(precise::m * precise::m, root(m4, 2));
EXPECT_EQ(precise::m, root(m4, 4));

EXPECT_EQ(precise::ft.root(0), precise::one);
EXPECT_EQ(root(precise::ft, 0), precise::one);
auto ft1 = precise::ft.pow(1);
EXPECT_EQ(precise::ft, ft1.root(1));
EXPECT_EQ(precise::ft.inv(), ft1.root(-1));
EXPECT_EQ(precise::ft, root(ft1, 1));
EXPECT_EQ(precise::ft.inv(), root(ft1, -1));

auto ft2 = precise::ft.pow(2);
EXPECT_EQ(precise::ft, ft2.root(2));
EXPECT_EQ(precise::ft.inv(), ft2.root(-2));
EXPECT_EQ(precise::ft, root(ft2, 2));
EXPECT_EQ(precise::ft.inv(), root(ft2, -2));
auto ft3 = precise::ft.pow(3);
EXPECT_EQ(precise::ft, ft3.root(3));
EXPECT_EQ(precise::ft.inv(), ft3.root(-3));
EXPECT_EQ(precise::ft, root(ft3, 3));
EXPECT_EQ(precise::ft.inv(), root(ft3, -3));
auto ft4 = precise::ft.pow(4);
EXPECT_EQ(precise::ft * precise::ft, ft4.root(2));
EXPECT_EQ(precise::ft, ft4.root(4));
EXPECT_EQ(precise::ft.inv(), ft4.root(-4));
EXPECT_EQ(precise::ft * precise::ft, root(ft4, 2));
EXPECT_EQ(precise::ft, root(ft4, 4));
EXPECT_EQ(precise::ft.inv(), root(ft4, -4));

auto ft5 = precise::ft.pow(5);
EXPECT_EQ(precise::ft, ft5.root(5));
EXPECT_EQ(precise::ft.inv(), ft5.root(-5));
EXPECT_EQ(precise::ft, root(ft5, 5));
EXPECT_EQ(precise::ft.inv(), root(ft5, -5));

EXPECT_TRUE(is_error((precise_unit(-4.5, m).root(2))));
EXPECT_TRUE(is_error(root(precise_unit(-4.5, m), 2)));
}

TEST(preciseUnitOps, nan)
Expand Down
Loading

0 comments on commit 4227623

Please sign in to comment.