diff --git a/src/include/units/bits/base_units_ratio.h b/src/include/units/bits/base_units_ratio.h index 06ef2a5c..50d505c5 100644 --- a/src/include/units/bits/base_units_ratio.h +++ b/src/include/units/bits/base_units_ratio.h @@ -32,7 +32,7 @@ template requires (E::den == 1 || E::den == 2) // TODO provide support for any den struct exp_ratio { using base_ratio = E::dimension::base_unit::ratio; - using positive_ratio = conditional, base_ratio>; + using positive_ratio = conditional, base_ratio>; static constexpr std::int64_t N = E::num * E::den < 0 ? -E::num : E::num; using pow = ratio_pow; using type = conditional, pow>; diff --git a/src/include/units/bits/unit_text.h b/src/include/units/bits/unit_text.h index bed0f58d..850e7f57 100644 --- a/src/include/units/bits/unit_text.h +++ b/src/include/units/bits/unit_text.h @@ -29,13 +29,19 @@ namespace units::detail { template constexpr auto ratio_text() { - if constexpr(Ratio::num != 1 || Ratio::den != 1) { + if constexpr(Ratio::num != 1 || Ratio::den != 1 || Ratio::exp != 0) { auto txt = basic_fixed_string("[") + regular(); - if constexpr(Ratio::den == 1) { + if constexpr(Ratio::den == 1 && Ratio::exp == 0) { return txt + basic_fixed_string("]"); } + else if constexpr (Ratio::den == 1) { + return txt + basic_fixed_string(" x 10") + superscript() + + basic_fixed_string("]"); + } else { - return txt + basic_fixed_string("/") + regular() + basic_fixed_string("]"); + return txt + basic_fixed_string("/") + regular() + + basic_fixed_string(" x 10") + superscript() + + basic_fixed_string("]"); } } else { @@ -46,7 +52,7 @@ constexpr auto ratio_text() template constexpr auto prefix_or_ratio_text() { - if constexpr(Ratio::num == 1 && Ratio::den == 1) { + if constexpr(Ratio::num == 1 && Ratio::den == 1 && Ratio::exp == 0) { // no ratio/prefix return basic_fixed_string(""); } diff --git a/src/include/units/physical/si/prefixes.h b/src/include/units/physical/si/prefixes.h index 6c09b90a..541ce36d 100644 --- a/src/include/units/physical/si/prefixes.h +++ b/src/include/units/physical/si/prefixes.h @@ -31,21 +31,23 @@ struct prefix : prefix_type {}; // TODO Remove dependency on std::ratio -struct atto : units::prefix> {}; -struct femto : units::prefix> {}; -struct pico : units::prefix> {}; -struct nano : units::prefix> {}; -struct micro : units::prefix> {}; -struct milli : units::prefix> {}; -struct centi : units::prefix> {}; -struct deci : units::prefix> {}; -struct deca : units::prefix> {}; -struct hecto : units::prefix> {}; -struct kilo : units::prefix> {}; -struct mega : units::prefix> {}; -struct giga : units::prefix> {}; -struct tera : units::prefix> {}; -struct peta : units::prefix> {}; -struct exa : units::prefix> {}; +// clang-format off +struct atto : units::prefix> {}; // std::atto::den>> {}; +struct femto : units::prefix> {}; // std::femto::den>> {}; +struct pico : units::prefix> {}; // std::pico::den>> {}; +struct nano : units::prefix> {}; // std::nano::den>> {}; +struct micro : units::prefix> {}; // std::micro::den>> {}; +struct milli : units::prefix> {}; // std::milli::den>> {}; +struct centi : units::prefix> {}; // std::centi::den>> {}; +struct deci : units::prefix> {}; // std::deci::den>> {}; +struct deca : units::prefix> {}; // std::deca::num>> {}; +struct hecto : units::prefix> {}; // std::hecto::num>> {}; +struct kilo : units::prefix> {}; // std::kilo::num>> {}; +struct mega : units::prefix> {}; // std::mega::num>> {}; +struct giga : units::prefix> {}; // std::giga::num>> {}; +struct tera : units::prefix> {}; // std::tera::num>> {}; +struct peta : units::prefix> {}; // std::peta::num>> {}; +struct exa : units::prefix> {}; // std::exa::num>> {}; +// clang-format off } // namespace units::si diff --git a/src/include/units/prefix.h b/src/include/units/prefix.h index eaded432..fb923521 100644 --- a/src/include/units/prefix.h +++ b/src/include/units/prefix.h @@ -30,14 +30,14 @@ namespace units { /** * @brief The base for all prefix types - * + * * Every prefix type should inherit from this type to satisfy PrefixType concept. */ struct prefix_type {}; /** * @brief No prefix possible for the unit - * + * * This is a special prefix type tag specifying that the unit can not be scaled with any kind * of the prefix. */ @@ -55,14 +55,14 @@ struct prefix_base : downcast_base> { /** * @brief A prefix used to scale units - * + * * Data from a prefix class is used in two cases: * - when defining a prefixed_unit its ratio is used to scale the reference unit and its * symbol is used to prepend to the symbol of referenced unit * - when printing the symbol of a scaled unit that was not predefined by the user but its * factor matches ratio of a prefix from the specified prefix family, its symbol will be * prepended to the symbol of the unit - * + * * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) * @tparam PT a type of prefix family * @tparam Symbol a text representation of the prefix @@ -70,7 +70,7 @@ struct prefix_base : downcast_base> { */ template requires (!std::same_as) -struct prefix : downcast_child>> { +struct prefix : downcast_child>> { static constexpr auto symbol = Symbol; }; diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index c8789c4e..ebcfe6ba 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -32,6 +32,7 @@ #endif #include +#include namespace units { @@ -51,10 +52,10 @@ concept safe_divisible = // exposition only /** * @brief A quantity - * + * * Property of a phenomenon, body, or substance, where the property has a magnitude that can be * expressed by means of a number and a measurement unit. - * + * * @tparam D a dimension of the quantity (can be either a BaseDimension or a DerivedDimension) * @tparam U a measurement unit of the quantity * @tparam Rep a type to be used to represent values of a quantity @@ -352,7 +353,7 @@ template; - return common_rep(lhs.count()) * common_rep(rhs.count()) * common_rep(ratio::num) / common_rep(ratio::den); + return common_rep(lhs.count()) * common_rep(rhs.count()) * common_rep(ratio::num) * std::pow(10, common_rep(ratio::exp)) / common_rep(ratio::den); } template @@ -376,7 +377,7 @@ template Expects(q.count() != 0); using dim = dim_invert; - using ratio = ratio; + using ratio = ratio; using unit = downcast_unit; using common_rep = decltype(v / q.count()); using ret = quantity; diff --git a/src/include/units/quantity_cast.h b/src/include/units/quantity_cast.h index 4906995b..ac019d82 100644 --- a/src/include/units/quantity_cast.h +++ b/src/include/units/quantity_cast.h @@ -24,6 +24,7 @@ #include #include +#include namespace units { @@ -41,10 +42,15 @@ struct quantity_cast_impl { { if constexpr (treat_as_floating_point) { return To(static_cast(static_cast(q.count()) * - (static_cast(CRatio::num) / static_cast(CRatio::den)))); + static_cast(std::pow(10, CRatio::exp)) * + (static_cast(CRatio::num) / + static_cast(CRatio::den)))); } else { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num) / - static_cast(CRatio::den))); + return To(static_cast(static_cast(q.count()) * + static_cast(CRatio::num) * + static_cast(CRatio::exp > 0 ? std::pow(10, CRatio::exp) : 1) / + (static_cast(CRatio::den) * + static_cast(CRatio::exp < 0 ? std::pow(10, -CRatio::exp) : 1)))); } } }; @@ -54,7 +60,7 @@ struct quantity_cast_impl { template static constexpr To cast(const Q& q) { - return To(static_cast(q.count())); + return To(static_cast(static_cast(q.count()) * static_cast(std::pow(10, CRatio::exp)))); } }; @@ -64,9 +70,9 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * (CRep{1} / static_cast(CRatio::den)))); + return To(static_cast(static_cast(q.count()) * static_cast(std::pow(10, CRatio::exp)) * (CRep{1} / static_cast(CRatio::den)))); } else { - return To(static_cast(static_cast(q.count()) / static_cast(CRatio::den))); + return To(static_cast(static_cast(q.count()) * static_cast(std::pow(10, CRatio::exp)) / static_cast(CRatio::den))); } } }; @@ -76,7 +82,7 @@ struct quantity_cast_impl { template static constexpr To cast(const Q& q) { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num))); + return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num) * static_cast(std::pow(10, CRatio::exp)))); } }; @@ -105,14 +111,14 @@ struct cast_ratio { /** * @brief Explcit cast of a quantity - * + * * Implicit conversions between quantities of different types are allowed only for "safe" * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. - * + * * This cast gets the target quantity type to cast to. For example: - * + * * auto q1 = units::quantity_cast>(1ms); - * + * * @tparam To a target quantity type to cast to */ template @@ -124,20 +130,20 @@ template using c_rep = std::common_type_t; using ret_unit = downcast_unit; using ret = quantity; - using cast = detail::quantity_cast_impl; + using cast = detail::quantity_cast_impl; return cast::cast(q); } /** * @brief Explcit cast of a quantity - * + * * Implicit conversions between quantities of different types are allowed only for "safe" * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. - * + * * This cast gets only the target dimension to cast to. For example: - * + * * auto q1 = units::quantity_cast(200Gal); - * + * * @tparam ToD a dimension type to use for a target quantity */ template @@ -149,14 +155,14 @@ template /** * @brief Explcit cast of a quantity - * + * * Implicit conversions between quantities of different types are allowed only for "safe" * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. - * + * * This cast gets only the target unit to cast to. For example: - * + * * auto q1 = units::quantity_cast(1ms); - * + * * @tparam ToU a unit type to use for a target quantity */ template @@ -168,14 +174,14 @@ template /** * @brief Explcit cast of a quantity - * + * * Implicit conversions between quantities of different types are allowed only for "safe" * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. - * + * * This cast gets only representation to cast to. For example: - * + * * auto q1 = units::quantity_cast(1ms); - * + * * @tparam ToRep a representation type to use for a target quantity */ template diff --git a/src/include/units/ratio.h b/src/include/units/ratio.h index 6ad15cef..f7dc4da6 100644 --- a/src/include/units/ratio.h +++ b/src/include/units/ratio.h @@ -27,6 +27,8 @@ #include #include #include +#include +#include namespace units { @@ -38,24 +40,45 @@ template return v < 0 ? -v : v; } +constexpr std::tuple normalize(std::intmax_t num, std::intmax_t den, std::intmax_t exp) { + std::intmax_t gcd = std::gcd(num, den); + num = num * (den < 0 ? -1 : 1) / gcd; + den = detail::abs(den) / gcd; + + while (num % 10 == 0) { + num /= 10; + ++exp; + } + while (den % 10 == 0) { + den /= 10; + --exp; + } + return std::make_tuple(num, den, exp); +} + } // namespace detail -template +template requires(Den != 0) struct ratio { static_assert(-INTMAX_MAX <= Num, "numerator too negative"); static_assert(-INTMAX_MAX <= Den, "denominator too negative"); - static constexpr std::intmax_t num = Num * (Den < 0 ? -1 : 1) / std::gcd(Num, Den); - static constexpr std::intmax_t den = detail::abs(Den) / std::gcd(Num, Den); + private: + static constexpr auto norm = detail::normalize(Num, Den, Exp); - using type = ratio; + public: + static constexpr std::intmax_t num = std::get<0>(norm); + static constexpr std::intmax_t den = std::get<1>(norm); + static constexpr std::intmax_t exp = std::get<2>(norm); + + using type = ratio; }; namespace detail { -template -inline constexpr bool is_ratio> = true; +template +inline constexpr bool is_ratio> = true; } // namespace detail @@ -97,10 +120,19 @@ private: static constexpr std::intmax_t gcd1 = std::gcd(R1::num, R2::den); static constexpr std::intmax_t gcd2 = std::gcd(R2::num, R1::den); + static constexpr auto norm = detail::normalize(safe_multiply(R1::num / gcd1, R2::num / gcd2), + safe_multiply(R1::den / gcd2, R2::den / gcd1), + R1::exp + R2::exp); + + static constexpr std::intmax_t norm_num = std::get<0>(norm); + static constexpr std::intmax_t norm_den = std::get<1>(norm); + static constexpr std::intmax_t norm_exp = std::get<2>(norm); + public: - using type = ratio; + using type = ratio; static constexpr std::intmax_t num = type::num; static constexpr std::intmax_t den = type::den; + static constexpr std::intmax_t exp = type::exp; }; } // namespace detail @@ -115,9 +147,10 @@ namespace detail { template struct ratio_divide_impl { static_assert(R2::num != 0, "division by 0"); - using type = ratio_multiply>; + using type = ratio_multiply>; static constexpr std::intmax_t num = type::num; static constexpr std::intmax_t den = type::den; + static constexpr std::intmax_t exp = type::exp; }; } // namespace detail @@ -168,6 +201,7 @@ static constexpr std::intmax_t sqrt_impl(std::intmax_t v) { return sqrt_impl(v, template struct ratio_sqrt_impl { + // TODO this is broken..need /2 logic on EXP using type = ratio; }; @@ -190,7 +224,7 @@ template struct common_ratio_impl { static constexpr std::intmax_t gcd_num = std::gcd(R1::num, R2::num); static constexpr std::intmax_t gcd_den = std::gcd(R1::den, R2::den); - using type = ratio; + using type = ratio; }; } // namespace detail diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index 8214bdd1..13ca5176 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -110,7 +110,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") { SECTION("in terms of base units") { - const length, metre>> q(123); + const length, metre>> q(123); stream << q; SECTION("iostream") @@ -131,7 +131,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("in terms of derived units") { - const energy, joule>> q(60); + const energy, joule>> q(60); stream << q; SECTION("iostream") @@ -296,7 +296,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { - CHECK(stream.str() == "8 [1/100]m³"); + CHECK(stream.str() == "8 [1 x 10⁻²]m³"); } SECTION("fmt with default format {} on a quantity") @@ -317,7 +317,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { - CHECK(stream.str() == "2 [60]Hz"); + CHECK(stream.str() == "2 [6 x 10¹]Hz"); } SECTION("fmt with default format {} on a quantity") @@ -338,7 +338,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { - CHECK(stream.str() == "10 [1/60]W"); + CHECK(stream.str() == "10 [1/6 x 10⁻¹]W"); } SECTION("fmt with default format {} on a quantity") @@ -359,7 +359,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { - CHECK(stream.str() == "30 [50/3]W"); + CHECK(stream.str() == "30 [1/6 x 10²]W"); } SECTION("fmt with default format {} on a quantity") @@ -404,7 +404,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { - CHECK(stream.str() == "8 [1000]m⋅s"); + CHECK(stream.str() == "8 [1 x 10³]m⋅s"); } SECTION("fmt with default format {} on a quantity") @@ -425,7 +425,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { - CHECK(stream.str() == "2 [60]kg/s"); + CHECK(stream.str() == "2 [6 x 10¹]kg/s"); } SECTION("fmt with default format {} on a quantity") @@ -446,7 +446,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { - CHECK(stream.str() == "10 [1/60]kg/s"); + CHECK(stream.str() == "10 [1/6 x 10⁻¹]kg/s"); } SECTION("fmt with default format {} on a quantity") @@ -467,7 +467,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("iostream") { - CHECK(stream.str() == "30 [3/50]1/m⋅s"); + CHECK(stream.str() == "30 [6 x 10⁻²]1/m⋅s"); } SECTION("fmt with default format {} on a quantity") @@ -693,7 +693,7 @@ TEST_CASE("%q an %Q can be put anywhere in a format string", "[text][fmt]") TEST_CASE("fill and align specification", "[text][fmt]") { - SECTION("default format {} on a quantity") + SECTION("default format {} on a quantity") { CHECK(fmt::format("|{:0}|", 123m) == "|123 m|"); CHECK(fmt::format("|{:10}|", 123m) == "| 123 m|"); @@ -716,8 +716,8 @@ TEST_CASE("fill and align specification", "[text][fmt]") CHECK(fmt::format("|{:*>10%Q%q}|", 123m) == "|******123m|"); CHECK(fmt::format("|{:*^10%Q%q}|", 123m) == "|***123m***|"); } - - SECTION("value only format {:%Q} on a quantity") + + SECTION("value only format {:%Q} on a quantity") { CHECK(fmt::format("|{:0%Q}|", 123m) == "|123|"); CHECK(fmt::format("|{:10%Q}|", 123m) == "| 123|"); @@ -729,7 +729,7 @@ TEST_CASE("fill and align specification", "[text][fmt]") CHECK(fmt::format("|{:*^10%Q}|", 123m) == "|***123****|"); } - SECTION("symbol only format {:%q} on a quantity") + SECTION("symbol only format {:%q} on a quantity") { CHECK(fmt::format("|{:0%q}|", 123m) == "|m|"); CHECK(fmt::format("|{:10%q}|", 123m) == "|m |"); @@ -747,7 +747,7 @@ TEST_CASE("sign specification", "[text][fmt]") length inf(std::numeric_limits::infinity()); length nan(std::numeric_limits::quiet_NaN()); - SECTION("default format {} on a quantity") + SECTION("default format {} on a quantity") { CHECK(fmt::format("{0:},{0:+},{0:-},{0: }", 1m) == "1 m,+1 m,1 m, 1 m"); CHECK(fmt::format("{0:},{0:+},{0:-},{0: }", -1m) == "-1 m,-1 m,-1 m,-1 m"); @@ -755,7 +755,7 @@ TEST_CASE("sign specification", "[text][fmt]") CHECK(fmt::format("{0:},{0:+},{0:-},{0: }", nan) == "nan m,+nan m,nan m, nan m"); } - SECTION("full format {:%Q %q} on a quantity") + SECTION("full format {:%Q %q} on a quantity") { CHECK(fmt::format("{0:%Q%q},{0:+%Q%q},{0:-%Q%q},{0: %Q%q}", 1m) == "1m,+1m,1m, 1m"); CHECK(fmt::format("{0:%Q%q},{0:+%Q%q},{0:-%Q%q},{0: %Q%q}", -1m) == "-1m,-1m,-1m,-1m"); @@ -763,7 +763,7 @@ TEST_CASE("sign specification", "[text][fmt]") CHECK(fmt::format("{0:%Q%q},{0:+%Q%q},{0:-%Q%q},{0: %Q%q}", nan) == "nanm,+nanm,nanm, nanm"); } - SECTION("value only format {:%Q} on a quantity") + SECTION("value only format {:%Q} on a quantity") { CHECK(fmt::format("{0:%Q},{0:+%Q},{0:-%Q},{0: %Q}", 1m) == "1,+1,1, 1"); CHECK(fmt::format("{0:%Q},{0:+%Q},{0:-%Q},{0: %Q}", -1m) == "-1,-1,-1,-1"); @@ -781,7 +781,7 @@ TEST_CASE("sign specification for unit only", "[text][fmt][exception]") TEST_CASE("precision specification", "[text][fmt]") { - SECTION("default format {} on a quantity") + SECTION("default format {} on a quantity") { CHECK(fmt::format("{:.1}", 1.2345m) == "1.2 m"); CHECK(fmt::format("{:.0}", 1.2345m) == "1 m"); @@ -792,7 +792,7 @@ TEST_CASE("precision specification", "[text][fmt]") CHECK(fmt::format("{:.10}", 1.2345m) == "1.2345000000 m"); } - SECTION("full format {:%Q %q} on a quantity") + SECTION("full format {:%Q %q} on a quantity") { CHECK(fmt::format("{:.0%Q %q}", 1.2345m) == "1 m"); CHECK(fmt::format("{:.1%Q %q}", 1.2345m) == "1.2 m"); @@ -803,7 +803,7 @@ TEST_CASE("precision specification", "[text][fmt]") CHECK(fmt::format("{:.10%Q %q}", 1.2345m) == "1.2345000000 m"); } - SECTION("value only format {:%Q} on a quantity") + SECTION("value only format {:%Q} on a quantity") { CHECK(fmt::format("{:.0%Q}", 1.2345m) == "1"); CHECK(fmt::format("{:.1%Q}", 1.2345m) == "1.2"); @@ -817,17 +817,17 @@ TEST_CASE("precision specification", "[text][fmt]") TEST_CASE("precision specification for integral representation should throw", "[text][fmt][exception]") { - SECTION("default format {} on a quantity") + SECTION("default format {} on a quantity") { REQUIRE_THROWS_MATCHES(fmt::format("{:.1}", 1m), fmt::format_error, Message("precision not allowed for integral quantity representation")); } - SECTION("full format {:%Q %q} on a quantity") + SECTION("full format {:%Q %q} on a quantity") { REQUIRE_THROWS_MATCHES(fmt::format("{:.1%Q %q}", 1m), fmt::format_error, Message("precision not allowed for integral quantity representation")); } - SECTION("value only format {:%Q} on a quantity") + SECTION("value only format {:%Q} on a quantity") { REQUIRE_THROWS_MATCHES(fmt::format("{:.1%Q}", 1m), fmt::format_error, Message("precision not allowed for integral quantity representation")); } diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index 7dd965f3..12a2706a 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -160,6 +160,7 @@ static_assert(length(1km).count() == 1000); // static_assert(length(1s).count() == 1); // should not compile (different dimensions) //static_assert(length(1010m).count() == 1); // should not compile (truncating conversion) static_assert(length(quantity_cast>(1010m)).count() == 1); +static_assert(length(quantity_cast>(1010m)).count() == 1000); // assignment operator @@ -236,23 +237,23 @@ static_assert(std::is_same_v()), length() * si::time()), length>); static_assert( - std::is_same_v() * si::time()), length, metre>, int>>); + std::is_same_v() * si::time()), length, metre>, int>>); static_assert(std::is_same_v() * si::time()), - quantity, units::exp>, scaled_unit, unknown_unit>>>); + quantity, units::exp>, scaled_unit, unknown_unit>>>); static_assert(std::is_same_v()), frequency>); -static_assert(std::is_same_v()), frequency, hertz>, int>>); +static_assert(std::is_same_v()), frequency, hertz>, int>>); static_assert(std::is_same_v()), si::time>); static_assert(std::is_same_v()), - quantity>, scaled_unit, unknown_unit>>>); + quantity>, scaled_unit, unknown_unit>>>); static_assert(std::is_same_v() / 1.0), length>); static_assert(std::is_same_v() / length()), double>); static_assert(std::is_same_v() / length()), double>); static_assert( std::is_same_v() / si::time()), velocity>); static_assert( - std::is_same_v() / si::time()), velocity, metre_per_second>>>); + std::is_same_v() / si::time()), velocity, metre_per_second>>>); static_assert(std::is_same_v() / length()), - quantity, units::exp>, scaled_unit, unknown_unit>>>); + quantity, units::exp>, scaled_unit, unknown_unit>>>); static_assert(std::is_same_v() % short(1)), length>); static_assert(std::is_same_v() % length(1)), length>); diff --git a/test/unit_test/static/ratio_test.cpp b/test/unit_test/static/ratio_test.cpp index ec1eae7a..7164a32b 100644 --- a/test/unit_test/static/ratio_test.cpp +++ b/test/unit_test/static/ratio_test.cpp @@ -27,10 +27,16 @@ using namespace units; template - inline constexpr bool same = R1::num == R2::num && R1::den == R2::den; + inline constexpr bool same = R1::num == R2::num && R1::den == R2::den && R1::exp == R2::exp; static_assert(same, ratio<1, 2>>); + // basic exponents tests + // note use of ::type is required because template params are changed while stamping out template + static_assert(std::is_same_v::type, ratio<1, 20, 1>::type>); + static_assert(std::is_same_v::type, ratio<10, 2, -1>::type>); + static_assert(std::is_same_v::type, ratio<20'000, 50, -1>::type>); + static_assert(std::is_same_v, ratio<3, 8>>, ratio<3, 8>>); static_assert(std::is_same_v, ratio<1>>, ratio<3, 8>>); static_assert(std::is_same_v, ratio<1, 8>>, ratio<1, 2>>); @@ -38,11 +44,19 @@ static_assert(std::is_same_v, ratio<2>>, ratio<1, 4>>); static_assert(std::is_same_v, ratio<8>>, ratio<4>>); + // multiply with exponents + static_assert(std::is_same_v, ratio<2, 1, 4>>, ratio<1, 4, 6>>); + static_assert(std::is_same_v, ratio<8, 1, 3>>, ratio<4, 1, -1>>); + static_assert(std::is_same_v, ratio<2>>, ratio<2>>); static_assert(std::is_same_v, ratio<8>>, ratio<1, 4>>); static_assert(std::is_same_v, ratio<2>>, ratio<1, 16>>); static_assert(std::is_same_v, ratio<3>>, ratio<2>>); + // divide with exponents + static_assert(std::is_same_v, ratio<2, 1, -8>>, ratio<1, 16, 2>>); + static_assert(std::is_same_v, ratio<3>>, ratio<2, 1, 4>>); + static_assert(std::is_same_v, 0>, ratio<1>>); static_assert(std::is_same_v, 1>, ratio<2>>); static_assert(std::is_same_v, 2>, ratio<4>>); @@ -52,17 +66,33 @@ static_assert(std::is_same_v, 2>, ratio<1, 4>>); static_assert(std::is_same_v, 3>, ratio<1, 8>>); + // pow with exponents + static_assert(std::is_same_v, 2>, ratio<1, 4, 6>>); + static_assert(std::is_same_v, 3>, ratio<1, 8, -18>>); + static_assert(std::is_same_v>, ratio<3>>); static_assert(std::is_same_v>, ratio<2>>); static_assert(std::is_same_v>, ratio<1>>); static_assert(std::is_same_v>, ratio<0>>); static_assert(std::is_same_v>, ratio<1, 2>>); + // // sqrt with exponents: TODO not working yet. Also not sure the non exponent version is accurate. + // static_assert(std::is_same_v>, ratio<3, 1, 1>>); + // static_assert(std::is_same_v>, ratio<2>>); + // common_ratio + // note use of ::type is required because template params are changed while stamping out template + static_assert(std::is_same_v::type, ratio<1000>>, ratio<1>::type>); + static_assert(std::is_same_v, ratio<1>>::type, ratio<1>::type>); + static_assert(std::is_same_v, ratio<1, 1000>>::type, ratio<1, 1000>::type>); + static_assert(std::is_same_v, ratio<1>>::type, ratio<1, 1000>::type>); + static_assert(std::is_same_v, ratio<10, 1>>::type, ratio<10, 1>::type>); + static_assert(std::is_same_v, ratio<1, 10>>::type, ratio<1, 10>::type>); - static_assert(std::is_same_v, ratio<1000>>, ratio<1>>); - static_assert(std::is_same_v, ratio<1>>, ratio<1>>); - static_assert(std::is_same_v, ratio<1, 1000>>, ratio<1, 1000>>); - static_assert(std::is_same_v, ratio<1>>, ratio<1, 1000>>); + // common ratio with exponents + static_assert(std::is_same_v, ratio<1, 1, 3>>::type, ratio<1>::type>); + static_assert(std::is_same_v, ratio<1, 1, -3>>::type, ratio<1, 1, -3>::type>); - } // namespace \ No newline at end of file + + + } // namespace diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp index a6a54fc2..62d7ab3d 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -192,7 +192,7 @@ static_assert(10V * 1F == 10C); // velocity -static_assert(std::is_same_v, metre_per_second>, std::int64_t>>); +static_assert(std::is_same_v, metre_per_second>, std::int64_t>>); static_assert(10m / 5s == 2mps); static_assert(10 / 5s * 1m == 2mps); diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index f7b7aa42..3be1ee5d 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -30,12 +30,12 @@ using namespace units; struct metre : named_unit {}; struct centimetre : prefixed_unit {}; struct kilometre : prefixed_unit {}; -struct yard : named_scaled_unit, metre> {}; +struct yard : named_scaled_unit, metre> {}; struct foot : named_scaled_unit, yard> {}; struct dim_length : base_dimension<"length", metre> {}; struct second : named_unit {}; -struct hour : named_scaled_unit, second> {}; +struct hour : named_scaled_unit, second> {}; struct dim_time : base_dimension<"time", second> {}; struct kelvin : named_unit {}; @@ -46,8 +46,8 @@ struct dim_velocity : derived_dimension {}; static_assert(std::is_same_v, metre>>, metre>); -static_assert(std::is_same_v, metre>>, centimetre>); -static_assert(std::is_same_v, metre>>, yard>); +static_assert(std::is_same_v, metre>>, centimetre>); +static_assert(std::is_same_v, metre>>, yard>); static_assert(std::is_same_v>, metre>>, foot>); static_assert(std::is_same_v, metre_per_second>>, kilometre_per_hour>);