diff --git a/include/xo/unit/Quantity.hpp b/include/xo/unit/Quantity.hpp index 84807aa4..fd91528d 100644 --- a/include/xo/unit/Quantity.hpp +++ b/include/xo/unit/Quantity.hpp @@ -5,7 +5,7 @@ #pragma once -#include "quantity2_concept.hpp" +#include "quantity_ops.hpp" #include "scaled_unit.hpp" #include "natural_unit.hpp" @@ -60,10 +60,6 @@ namespace xo { ratio_int2x_type>(this->unit_, unit2); if (rr.natural_unit_.is_dimensionless()) { - /* FIXME: rr.outer_scale_exact_ can overflow since fixed precision. - * accumulate in scale instead - */ - repr_type r_scale = (::sqrt(rr.outer_scale_sq_) * rr.outer_scale_factor_.template convert_to() * this->scale_); @@ -98,7 +94,7 @@ namespace xo { typename Quantity2::repr_type>; using r_int_type = std::common_type_t; - using r_int2x_type = std::common_type_t; auto rr = detail::su_product(x.unit(), y.unit()); @@ -216,6 +212,8 @@ namespace xo { return *this; } + // TODO: operator+=, operator-= + constexpr nu_abbrev_type abbrev() const { return unit_.abbrev(); } private: @@ -245,34 +243,6 @@ namespace xo { return Quantity(1.0, nu); } - /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr) - **/ - template - requires quantity2_concept && quantity2_concept - constexpr auto - operator* (const Quantity & x, const Quantity2 & y) - { - return Quantity::multiply(x, y); - } - - /** note: does not require unit scaling, so constexpr with c++23 **/ - template - requires std::is_arithmetic_v && quantity2_concept - constexpr auto - operator* (Dimensionless x, const Quantity & y) - { - return y.scale_by(x); - } - - /** note: does not require unit scaling, so constexpr with c++23 **/ - template - requires std::is_arithmetic_v && quantity2_concept - constexpr auto - operator* (const Quantity & x, Dimensionless y) - { - return x.scale_by(y); - } - /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr) **/ template @@ -321,16 +291,6 @@ namespace xo { return Quantity::subtract(x, y); } - /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr) - **/ - template - requires quantity2_concept && quantity2_concept - constexpr auto - operator== (const Quantity & x, const Quantity2 & y) - { - return (Quantity::compare(x, y) == 0); - } - /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr) **/ template diff --git a/include/xo/unit/natural_unit.hpp b/include/xo/unit/natural_unit.hpp index 7617aade..47a25bd4 100644 --- a/include/xo/unit/natural_unit.hpp +++ b/include/xo/unit/natural_unit.hpp @@ -65,8 +65,9 @@ namespace xo { public: constexpr natural_unit() : n_bpu_{0} {} - static constexpr natural_unit from_bu(basis_unit bu) { - return detail::nu_maker::make_nu(make_unit_power(bu)); + static constexpr natural_unit from_bu(basis_unit bu, + power_ratio_type power = power_ratio_type(1)) { + return detail::nu_maker::make_nu(bpu(bu, power)); } constexpr std::size_t n_bpu() const { return n_bpu_; } @@ -399,6 +400,9 @@ namespace xo { constexpr auto currency = natural_unit::from_bu(bu::currency); constexpr auto price = natural_unit::from_bu(bu::price); + + constexpr auto volatility_250d = natural_unit::from_bu(bu::year250, power_ratio_type(-1,2)); + constexpr auto volatility_360d = natural_unit::from_bu(bu::year360, power_ratio_type(-1,2)); } /*namespace nu*/ } /*namespace qty*/ } /*namespace xo*/ diff --git a/include/xo/unit/quantity.hpp b/include/xo/unit/quantity.hpp index a6dc0391..a121afe5 100644 --- a/include/xo/unit/quantity.hpp +++ b/include/xo/unit/quantity.hpp @@ -5,6 +5,7 @@ #pragma once +#include "quantity_ops.hpp" #include "natural_unit.hpp" #include "scaled_unit.hpp" @@ -20,7 +21,7 @@ namespace xo { typename Repr = double, typename Int = std::int64_t, natural_unit NaturalUnit = natural_unit(), - typename Int2x = detail::width2x + typename Int2x = detail::width2x_t > class quantity { public: @@ -36,6 +37,86 @@ namespace xo { constexpr const repr_type & scale() const { return scale_; } constexpr const unit_type & unit() const { return s_unit; } + // is_dimensionless + + // unit_qty + // zero_qty + // reciprocal + + /* parallel implementation to Quantity::rescale(), + * except that NaturalUnit2 is a compile-time-only template-argument + * + * NOTE: constexpr as long as no fractional units involved. + */ + template NaturalUnit2> + constexpr + auto rescale() const { + /* conversion factor from .unit -> unit2*/ + auto rr = detail::su_ratio(NaturalUnit, NaturalUnit2); + + if (rr.natural_unit_.is_dimensionless()) { + repr_type r_scale = (((rr.outer_scale_sq_ == 1.0) + ? 1.0 + : ::sqrt(rr.outer_scale_sq_)) + * rr.outer_scale_factor_.template convert_to() + * this->scale_); + return quantity(r_scale); + } else { + return quantity(std::numeric_limits::quiet_NaN()); + } + } + + template + requires std::is_arithmetic_v + constexpr auto scale_by(Dimensionless x) const { + return quantity(x * this->scale_); + } + + // divide_by + // divide_into + + /* parallel implementation to Quantity, + * but return type will have dimension computed at compile-time + */ + template + static constexpr auto multiply(const quantity & x, const Quantity2 & y) { + using r_repr_type = std::common_type_t; + using r_int_type = std::common_type_t; + using r_int2x_type = std::common_type_t; + + constexpr auto rr = detail::su_product(x.unit(), y.unit()); + + r_repr_type r_scale = (::sqrt(rr.outer_scale_sq_) + * rr.outer_scale_factor_.template convert_to() + * static_cast(x.scale()) + * static_cast(y.scale())); + + return quantity(r_scale); + } + + // divide + // add + // subtract + + /* parallel implementation to Quantity */ + template + static constexpr + auto compare(const quantity &x, const Quantity2 & y) { + quantity y2 = y.template rescale(); + + return x.scale() <=> y2.scale(); + } + + // operator- + // operator+= + // operator-= + // operator*= + // operator/= + constexpr nu_abbrev_type abbrev() const { return s_unit.abbrev(); } public: /* need public members so that a quantity instance can be a non-type template parameter (is a structural type) */ @@ -89,6 +170,12 @@ namespace xo { inline constexpr auto year360s(double x) { return quantity(x); } inline constexpr auto year365s(double x) { return quantity(x); } //inline constexpr auto year366s(double x) { return quantity(x); } + + // ----- volatility ----- + + /* volatility in units of 1/yr */ + inline constexpr auto volatility_250d(double x) { return quantity(x); } + inline constexpr auto volatility_360d(double x) { return quantity(x); } } } /*namespace qty*/ } /*namespace xo*/ diff --git a/include/xo/unit/quantity2_concept.hpp b/include/xo/unit/quantity2_concept.hpp index 1449539f..9d6c3922 100644 --- a/include/xo/unit/quantity2_concept.hpp +++ b/include/xo/unit/quantity2_concept.hpp @@ -13,6 +13,8 @@ namespace xo { typename Quantity::unit_type; typename Quantity::repr_type; + //{ Quantity::multiply(qty, qty) }; + { qty.scale() } -> std::same_as; { qty.unit() } -> std::same_as; //{ Quantity::unit_cstr() } -> std::same_as; diff --git a/include/xo/unit/quantity_ops.hpp b/include/xo/unit/quantity_ops.hpp new file mode 100644 index 00000000..63be47c0 --- /dev/null +++ b/include/xo/unit/quantity_ops.hpp @@ -0,0 +1,55 @@ +/** @file quantity_ops.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "quantity2_concept.hpp" +//#include + +namespace xo { + namespace qty { + /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr) + **/ + template + requires quantity2_concept && quantity2_concept + constexpr auto + operator* (const Quantity & x, const Quantity2 & y) + { + return Quantity::multiply(x, y); + } + + /** note: does not require unit scaling, so constexpr with c++23 **/ + template + requires std::is_arithmetic_v && quantity2_concept + constexpr auto + operator* (Dimensionless x, const Quantity & y) + { + return y.scale_by(x); + } + + /** note: does not require unit scaling, so constexpr with c++23 **/ + template + requires std::is_arithmetic_v && quantity2_concept + constexpr auto + operator* (const Quantity & x, Dimensionless y) + { + return x.scale_by(y); + } + + /** note: won't have constexpr result until c++26 (when ::sqrt(), ::pow() are constexpr) + **/ + template + requires quantity2_concept && quantity2_concept + constexpr auto + operator== (const Quantity & x, const Quantity2 & y) + { + return (Quantity::compare(x, y) == 0); + } + } /*namespace qty*/ + +} /*namespace xo*/ + + +/** end quantity_ops.hpp **/ diff --git a/utest/quantity.test.cpp b/utest/quantity.test.cpp index ee232477..7104bc46 100644 --- a/utest/quantity.test.cpp +++ b/utest/quantity.test.cpp @@ -314,6 +314,63 @@ namespace xo { REQUIRE(tostr(yr365) == "1yr365"); } /*TEST_CASE(quantity.time)*/ + TEST_CASE("quantity.mult", "[quantity.mult]") { + constexpr auto pg = qty::picograms(1.0); + constexpr auto ng = qty::nanograms(1.0); + constexpr auto ug = qty::micrograms(1.0); + + constexpr auto ng_in_pg = ng.rescale(); + + static_assert(ng_in_pg.scale() == 1000); + static_assert(ng_in_pg == pg * 1000); + + /* multiplication by dimensionless values is constexpr in c++23 + * comparison in not constexpr until c++26 + */ + + /* picograms:nanograms */ + + static_assert(pg * 1000 == ng); + REQUIRE(pg * 1000 == ng); + static_assert(1000 * pg == ng); + REQUIRE(1000 * pg == ng); + static_assert(ng * 0.001 == pg); + REQUIRE(ng * 0.001 == pg); + static_assert(0.001 * ng == pg); + REQUIRE(0.001 * ng == pg); + + /* picograms:micrograms */ + + static_assert(pg * 1e6 == ug); + REQUIRE(pg * 1e6 == ug); + static_assert(1e6 * pg == ug); + REQUIRE(1e6 * pg == ug); + static_assert(ug * 1e-6 == pg); + REQUIRE(ug * 1e-6 == pg); + static_assert(1e-6 * ug == pg); + REQUIRE(1e-6 * ug == pg); + + /* nanograms:micrograms */ + + static_assert(ng * 1e3 == ug); + REQUIRE(ng * 1e3 == ug); + static_assert(1e3 * ng == ug); + REQUIRE(1e3 * ng == ug); + static_assert(ug * 1e-3 == ng); + REQUIRE(ug * 1e-3 == ng); + static_assert(1e-3 * ug == ng); + REQUIRE(1e-3 * ug == ng); + + // /* picograms:milligrams */ + // /* nanograms:milligrams */ + // /* micrograms:milligrams */ + + // /* picograms:grams */ + // /* nanograms:grams */ + // /* micrograms:grams */ + // /* milligrams:grams */ + } /*TEST_CASE(quantity.mult)*/ + } /*namespace qty*/ } /*namespace xo*/