From 407a13c48fad18c767f7b1d5d432b6596294e52b Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Thu, 7 Jul 2022 16:10:34 +0000 Subject: [PATCH 1/5] Add `extract_power_of_10(Magnitude)` utility This will help with pretty-printing magnitudes by extracting a reasonable power of 10. --- src/core/include/units/magnitude.h | 22 +++++++++++++++++++ test/unit_test/runtime/magnitude_test.cpp | 26 +++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index ddf18cce..1257e132 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -656,4 +656,26 @@ constexpr Magnitude auto as_magnitude() detail::prime_factorization_v; } +namespace detail { +template +constexpr ratio get_power(T base, magnitude) +{ + return ((BPs.get_base() == base ? BPs.power : ratio{0}) + ... + ratio{0}); +} + +constexpr std::intmax_t integer_part(ratio r) { return numerator(r) / denominator(r); } + +constexpr std::intmax_t extract_power_of_10(Magnitude auto m) +{ + const auto power_of_2 = get_power(2, m); + const auto power_of_5 = get_power(5, m); + + if ((power_of_2 * power_of_5).num <= 0) { + return 0; + } + + return integer_part((detail::abs(power_of_2) < detail::abs(power_of_5)) ? power_of_2 : power_of_5); +} +} // namespace detail + } // namespace units diff --git a/test/unit_test/runtime/magnitude_test.cpp b/test/unit_test/runtime/magnitude_test.cpp index 0715df8f..9fa0aa98 100644 --- a/test/unit_test/runtime/magnitude_test.cpp +++ b/test/unit_test/runtime/magnitude_test.cpp @@ -610,4 +610,30 @@ TEST_CASE("strictly_increasing") } } +TEST_CASE("extract_power_of_10") +{ + SECTION("Picks out positive powers") + { + CHECK(extract_power_of_10(as_magnitude<10>()) == 1); + CHECK(extract_power_of_10(as_magnitude<20>()) == 1); + CHECK(extract_power_of_10(as_magnitude<40>()) == 1); + CHECK(extract_power_of_10(as_magnitude<50>()) == 1); + CHECK(extract_power_of_10(as_magnitude<100>()) == 2); + } + + SECTION("Picks out negative powers") + { + constexpr auto ONE = as_magnitude<1>(); + CHECK(extract_power_of_10(ONE / as_magnitude<10>()) == -1); + CHECK(extract_power_of_10(ONE / as_magnitude<20>()) == -1); + CHECK(extract_power_of_10(ONE / as_magnitude<40>()) == -1); + CHECK(extract_power_of_10(ONE / as_magnitude<50>()) == -1); + CHECK(extract_power_of_10(ONE / as_magnitude<100>()) == -2); + } + + SECTION("Zero if signs disagree") { CHECK(extract_power_of_10(as_magnitude<2>() / as_magnitude<5>()) == 0); } + + SECTION("Handles rational powers") { CHECK(extract_power_of_10(sqrt(as_magnitude<1000>())) == 1); } +} + } // namespace From e1a17ab1f0bf25968fa8c421dc1d9e737a5fc018 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Thu, 7 Jul 2022 16:33:51 +0000 Subject: [PATCH 2/5] Use variables instead of ratio members This prepares us for a time when we won't _have_ the `ratio` members (because we'll be using `Magnitude`). --- src/core/include/units/bits/unit_text.h | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/core/include/units/bits/unit_text.h b/src/core/include/units/bits/unit_text.h index 45345468..49c1c3e1 100644 --- a/src/core/include/units/bits/unit_text.h +++ b/src/core/include/units/bits/unit_text.h @@ -35,21 +35,25 @@ inline constexpr basic_symbol_text base_multiplier("\u00D7 10", "x 10"); template constexpr auto ratio_text() { - if constexpr (R.num == 1 && R.den == 1 && R.exp != 0) { - return base_multiplier + superscript(); - } else if constexpr (R.num != 1 || R.den != 1 || R.exp != 0) { - auto txt = basic_fixed_string("[") + regular(); - if constexpr (R.den == 1) { - if constexpr (R.exp == 0) { + constexpr auto num_value = R.num; + constexpr auto den_value = R.den; + constexpr auto exp10 = R.exp; + + if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) { + return base_multiplier + superscript(); + } else if constexpr (num_value != 1 || den_value != 1 || exp10 != 0) { + auto txt = basic_fixed_string("[") + regular(); + if constexpr (den_value == 1) { + if constexpr (exp10 == 0) { return txt + basic_fixed_string("]"); } else { - return txt + " " + base_multiplier + superscript() + basic_fixed_string("]"); + return txt + " " + base_multiplier + superscript() + basic_fixed_string("]"); } } else { - if constexpr (R.exp == 0) { - return txt + basic_fixed_string("/") + regular() + basic_fixed_string("]"); + if constexpr (exp10 == 0) { + return txt + basic_fixed_string("/") + regular() + basic_fixed_string("]"); } else { - return txt + basic_fixed_string("/") + regular() + " " + base_multiplier + superscript() + + return txt + basic_fixed_string("/") + regular() + " " + base_multiplier + superscript() + basic_fixed_string("]"); } } From 92750072b0ee56116fa49f8c04835cbd89269239 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Thu, 7 Jul 2022 16:44:19 +0000 Subject: [PATCH 3/5] Reimplement `ratio_text()` as `magnitude_text()` The algorithm is exactly the same; we just get our values for `num_value`, `den_value`, and `exp10` in different ways. The old printing logic assumed the ratio/Magnitude was an exact rational number. Formerly, we couldn't even _represent_ anything else, but with Magnitude, we can. Thus, we guard this assumption with a `static_assert`. Later on, we can figure out how we want to print irrational bases and/or rational powers. The point of this change is to unblock us from moving to the new infrastructure. --- src/core/include/units/bits/unit_text.h | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/core/include/units/bits/unit_text.h b/src/core/include/units/bits/unit_text.h index 49c1c3e1..736492e1 100644 --- a/src/core/include/units/bits/unit_text.h +++ b/src/core/include/units/bits/unit_text.h @@ -32,12 +32,19 @@ namespace units::detail { inline constexpr basic_symbol_text base_multiplier("\u00D7 10", "x 10"); -template -constexpr auto ratio_text() +template +constexpr auto magnitude_text() { - constexpr auto num_value = R.num; - constexpr auto den_value = R.den; - constexpr auto exp10 = R.exp; + constexpr auto exp10 = extract_power_of_10(M); + + constexpr Magnitude auto base = M / pow(as_magnitude<10>()); + constexpr Magnitude auto num = numerator(base); + constexpr Magnitude auto den = denominator(base); + static_assert(base == num / den, "Printing rational powers, or irrational bases, not yet supported"); + + constexpr auto num_value = get_value(num); + constexpr auto den_value = get_value(den); + if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) { return base_multiplier + superscript(); @@ -62,6 +69,12 @@ constexpr auto ratio_text() } } +template +constexpr auto ratio_text() +{ + return magnitude_text()>(); +} + template constexpr auto prefix_or_ratio_text() { From e1a173a02d8d5ec790ad76013dfa3944f65e833f Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Thu, 7 Jul 2022 17:09:42 +0000 Subject: [PATCH 4/5] Migrate prefixes to Magnitude --- src/core/include/units/bits/basic_concepts.h | 6 +-- src/core/include/units/bits/unit_text.h | 20 ++++------ src/core/include/units/prefix.h | 10 ++--- .../units/isq/iec80000/binary_prefixes.h | 12 +++--- .../si/include/units/isq/si/prefixes.h | 40 +++++++++---------- 5 files changed, 41 insertions(+), 47 deletions(-) diff --git a/src/core/include/units/bits/basic_concepts.h b/src/core/include/units/bits/basic_concepts.h index dfe207f7..28776ab9 100644 --- a/src/core/include/units/bits/basic_concepts.h +++ b/src/core/include/units/bits/basic_concepts.h @@ -40,11 +40,11 @@ namespace units { // Prefix namespace detail { -template +template struct prefix_base; -template -void to_prefix_base(const volatile prefix_base*); +template +void to_prefix_base(const volatile prefix_base*); } // namespace detail diff --git a/src/core/include/units/bits/unit_text.h b/src/core/include/units/bits/unit_text.h index 736492e1..f546a251 100644 --- a/src/core/include/units/bits/unit_text.h +++ b/src/core/include/units/bits/unit_text.h @@ -69,28 +69,22 @@ constexpr auto magnitude_text() } } -template -constexpr auto ratio_text() +template +constexpr auto prefix_or_magnitude_text() { - return magnitude_text()>(); -} - -template -constexpr auto prefix_or_ratio_text() -{ - if constexpr (R.num == 1 && R.den == 1 && R.exp == 0) { + if constexpr (M == as_magnitude<1>()) { // no ratio/prefix return basic_fixed_string(""); } else { // try to form a prefix - using prefix = downcast>; + using prefix = downcast>; - if constexpr (can_be_prefixed && !is_same_v>) { + if constexpr (can_be_prefixed && !is_same_v>) { // print as a prefixed unit return prefix::symbol; } else { // print as a ratio of the coherent unit - constexpr auto txt = ratio_text(); + constexpr auto txt = magnitude_text(); if constexpr (SymbolLen > 0 && txt.standard().size() > 0) return txt + basic_fixed_string(" "); else @@ -164,7 +158,7 @@ constexpr auto unit_text() }(); constexpr auto prefix_txt = - prefix_or_ratio_text(); + prefix_or_magnitude_text(); return prefix_txt + symbol_text; } } diff --git a/src/core/include/units/prefix.h b/src/core/include/units/prefix.h index 0a95499c..803cf341 100644 --- a/src/core/include/units/prefix.h +++ b/src/core/include/units/prefix.h @@ -34,9 +34,9 @@ namespace units { namespace detail { -template -struct prefix_base : downcast_base> { - static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = as_magnitude(); +template +struct prefix_base : downcast_base> { + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = M; }; } // namespace detail @@ -54,8 +54,8 @@ struct prefix_base : downcast_base> { * @tparam Symbol a text representation of the prefix * @tparam R factor to be used to scale a unit */ -template -struct prefix : downcast_dispatch, downcast_mode::on> { +template +struct prefix : downcast_dispatch, downcast_mode::on> { static constexpr auto symbol = Symbol; }; diff --git a/src/systems/isq-iec80000/include/units/isq/iec80000/binary_prefixes.h b/src/systems/isq-iec80000/include/units/isq/iec80000/binary_prefixes.h index f5e472ef..e1128357 100644 --- a/src/systems/isq-iec80000/include/units/isq/iec80000/binary_prefixes.h +++ b/src/systems/isq-iec80000/include/units/isq/iec80000/binary_prefixes.h @@ -26,11 +26,11 @@ namespace units::isq::iec80000 { -struct kibi : prefix {}; -struct mebi : prefix {}; -struct gibi : prefix {}; -struct tebi : prefix {}; -struct pebi : prefix {}; -struct exbi : prefix {}; +struct kibi : prefix(as_magnitude<2>())> {}; +struct mebi : prefix(as_magnitude<2>())> {}; +struct gibi : prefix(as_magnitude<2>())> {}; +struct tebi : prefix(as_magnitude<2>())> {}; +struct pebi : prefix(as_magnitude<2>())> {}; +struct exbi : prefix(as_magnitude<2>())> {}; } // namespace units::isq::iec80000 diff --git a/src/systems/si/include/units/isq/si/prefixes.h b/src/systems/si/include/units/isq/si/prefixes.h index 81f7c553..5ee1ec04 100644 --- a/src/systems/si/include/units/isq/si/prefixes.h +++ b/src/systems/si/include/units/isq/si/prefixes.h @@ -27,26 +27,26 @@ namespace units::isq::si { // clang-format off -struct yocto : prefix {}; -struct zepto : prefix {}; -struct atto : prefix {}; -struct femto : prefix {}; -struct pico : prefix {}; -struct nano : prefix {}; -struct micro : prefix {}; -struct milli : prefix {}; -struct centi : prefix {}; -struct deci : prefix {}; -struct deca : prefix {}; -struct hecto : prefix {}; -struct kilo : prefix {}; -struct mega : prefix {}; -struct giga : prefix {}; -struct tera : prefix {}; -struct peta : prefix {}; -struct exa : prefix {}; -struct zetta : prefix {}; -struct yotta : prefix {}; +struct yocto : prefix(as_magnitude<10>())> {}; +struct zepto : prefix(as_magnitude<10>())> {}; +struct atto : prefix(as_magnitude<10>())> {}; +struct femto : prefix(as_magnitude<10>())> {}; +struct pico : prefix(as_magnitude<10>())> {}; +struct nano : prefix(as_magnitude<10>())> {}; +struct micro : prefix(as_magnitude<10>())> {}; +struct milli : prefix(as_magnitude<10>())> {}; +struct centi : prefix(as_magnitude<10>())> {}; +struct deci : prefix(as_magnitude<10>())> {}; +struct deca : prefix(as_magnitude<10>())> {}; +struct hecto : prefix(as_magnitude<10>())> {}; +struct kilo : prefix(as_magnitude<10>())> {}; +struct mega : prefix(as_magnitude<10>())> {}; +struct giga : prefix(as_magnitude<10>())> {}; +struct tera : prefix(as_magnitude<10>())> {}; +struct peta : prefix(as_magnitude<10>())> {}; +struct exa : prefix(as_magnitude<10>())> {}; +struct zetta : prefix(as_magnitude<10>())> {}; +struct yotta : prefix(as_magnitude<10>())> {}; // clang-format on } // namespace units::isq::si From fd7b4df29d91625e28c79dc849f136d6a149e7ff Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Thu, 7 Jul 2022 16:52:37 -0400 Subject: [PATCH 5/5] Add zebi and yobi prefixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switching to Magnitude makes these easy. Co-authored-by: Johel Ernesto Guerrero Peña --- .../isq-iec80000/include/units/isq/iec80000/binary_prefixes.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/systems/isq-iec80000/include/units/isq/iec80000/binary_prefixes.h b/src/systems/isq-iec80000/include/units/isq/iec80000/binary_prefixes.h index e1128357..25fdaaa9 100644 --- a/src/systems/isq-iec80000/include/units/isq/iec80000/binary_prefixes.h +++ b/src/systems/isq-iec80000/include/units/isq/iec80000/binary_prefixes.h @@ -32,5 +32,7 @@ struct gibi : prefix(as_magnitude<2>())> {}; struct tebi : prefix(as_magnitude<2>())> {}; struct pebi : prefix(as_magnitude<2>())> {}; struct exbi : prefix(as_magnitude<2>())> {}; +struct zebi : prefix(as_magnitude<2>())> {}; +struct yobi : prefix(as_magnitude<2>())> {}; } // namespace units::isq::iec80000