Merge pull request #368 from chiphogg/chiphogg/print-mag

Migrate remaining ratio template parameters to Magnitude
This commit is contained in:
Mateusz Pusz
2022-07-19 19:10:06 +02:00
committed by GitHub
7 changed files with 114 additions and 53 deletions

View File

@@ -40,11 +40,11 @@ namespace units {
// Prefix
namespace detail {
template<ratio R>
template<Magnitude auto M>
struct prefix_base;
template<ratio R>
void to_prefix_base(const volatile prefix_base<R>*);
template<Magnitude auto M>
void to_prefix_base(const volatile prefix_base<M>*);
} // namespace detail

View File

@@ -32,24 +32,35 @@ namespace units::detail {
inline constexpr basic_symbol_text base_multiplier("\u00D7 10", "x 10");
template<ratio R>
constexpr auto ratio_text()
template<Magnitude auto M>
constexpr auto magnitude_text()
{
if constexpr (R.num == 1 && R.den == 1 && R.exp != 0) {
return base_multiplier + superscript<R.exp>();
} else if constexpr (R.num != 1 || R.den != 1 || R.exp != 0) {
auto txt = basic_fixed_string("[") + regular<R.num>();
if constexpr (R.den == 1) {
if constexpr (R.exp == 0) {
constexpr auto exp10 = extract_power_of_10(M);
constexpr Magnitude auto base = M / pow<exp10>(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<std::intmax_t>(num);
constexpr auto den_value = get_value<std::intmax_t>(den);
if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) {
return base_multiplier + superscript<exp10>();
} else if constexpr (num_value != 1 || den_value != 1 || exp10 != 0) {
auto txt = basic_fixed_string("[") + regular<num_value>();
if constexpr (den_value == 1) {
if constexpr (exp10 == 0) {
return txt + basic_fixed_string("]");
} else {
return txt + " " + base_multiplier + superscript<R.exp>() + basic_fixed_string("]");
return txt + " " + base_multiplier + superscript<exp10>() + basic_fixed_string("]");
}
} else {
if constexpr (R.exp == 0) {
return txt + basic_fixed_string("/") + regular<R.den>() + basic_fixed_string("]");
if constexpr (exp10 == 0) {
return txt + basic_fixed_string("/") + regular<den_value>() + basic_fixed_string("]");
} else {
return txt + basic_fixed_string("/") + regular<R.den>() + " " + base_multiplier + superscript<R.exp>() +
return txt + basic_fixed_string("/") + regular<den_value>() + " " + base_multiplier + superscript<exp10>() +
basic_fixed_string("]");
}
}
@@ -58,22 +69,22 @@ constexpr auto ratio_text()
}
}
template<Unit U, ratio R, std::size_t SymbolLen>
constexpr auto prefix_or_ratio_text()
template<Unit U, Magnitude auto M, std::size_t SymbolLen>
constexpr auto prefix_or_magnitude_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<detail::prefix_base<R>>;
using prefix = downcast<detail::prefix_base<M>>;
if constexpr (can_be_prefixed<U> && !is_same_v<prefix, prefix_base<R>>) {
if constexpr (can_be_prefixed<U> && !is_same_v<prefix, prefix_base<M>>) {
// print as a prefixed unit
return prefix::symbol;
} else {
// print as a ratio of the coherent unit
constexpr auto txt = ratio_text<R>();
constexpr auto txt = magnitude_text<M>();
if constexpr (SymbolLen > 0 && txt.standard().size() > 0)
return txt + basic_fixed_string(" ");
else
@@ -147,7 +158,7 @@ constexpr auto unit_text()
}();
constexpr auto prefix_txt =
prefix_or_ratio_text<U, as_ratio(U::mag / coherent_unit::mag), symbol_text.standard().size()>();
prefix_or_magnitude_text<U, U::mag / coherent_unit::mag, symbol_text.standard().size()>();
return prefix_txt + symbol_text;
}
}

View File

@@ -656,4 +656,26 @@ constexpr Magnitude auto as_magnitude()
detail::prime_factorization_v<R.den>;
}
namespace detail {
template<typename T, BasePower auto... BPs>
constexpr ratio get_power(T base, magnitude<BPs...>)
{
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

View File

@@ -34,9 +34,9 @@ namespace units {
namespace detail {
template<ratio R>
struct prefix_base : downcast_base<prefix_base<R>> {
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = as_magnitude<R>();
template<Magnitude auto M>
struct prefix_base : downcast_base<prefix_base<M>> {
static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = M;
};
} // namespace detail
@@ -54,8 +54,8 @@ struct prefix_base : downcast_base<prefix_base<R>> {
* @tparam Symbol a text representation of the prefix
* @tparam R factor to be used to scale a unit
*/
template<typename Child, basic_symbol_text Symbol, ratio R>
struct prefix : downcast_dispatch<Child, detail::prefix_base<R>, downcast_mode::on> {
template<typename Child, basic_symbol_text Symbol, Magnitude auto M>
struct prefix : downcast_dispatch<Child, detail::prefix_base<M>, downcast_mode::on> {
static constexpr auto symbol = Symbol;
};

View File

@@ -26,11 +26,13 @@
namespace units::isq::iec80000 {
struct kibi : prefix<kibi, "Ki", ratio(1'024)> {};
struct mebi : prefix<mebi, "Mi", ratio(1'048'576)> {};
struct gibi : prefix<gibi, "Gi", ratio(1'073'741'824)> {};
struct tebi : prefix<tebi, "Ti", ratio(1'099'511'627'776)> {};
struct pebi : prefix<pebi, "Pi", ratio(1'125'899'906'842'624)> {};
struct exbi : prefix<exbi, "Ei", ratio(1'152'921'504'606'846'976)> {};
struct kibi : prefix<kibi, "Ki", pow<10>(as_magnitude<2>())> {};
struct mebi : prefix<mebi, "Mi", pow<20>(as_magnitude<2>())> {};
struct gibi : prefix<gibi, "Gi", pow<30>(as_magnitude<2>())> {};
struct tebi : prefix<tebi, "Ti", pow<40>(as_magnitude<2>())> {};
struct pebi : prefix<pebi, "Pi", pow<50>(as_magnitude<2>())> {};
struct exbi : prefix<exbi, "Ei", pow<60>(as_magnitude<2>())> {};
struct zebi : prefix<zebi, "Zi", pow<70>(as_magnitude<2>())> {};
struct yobi : prefix<yobi, "Yi", pow<80>(as_magnitude<2>())> {};
} // namespace units::isq::iec80000

View File

@@ -27,26 +27,26 @@
namespace units::isq::si {
// clang-format off
struct yocto : prefix<yocto, "y", ratio(1, 1, -24)> {};
struct zepto : prefix<zepto, "z", ratio(1, 1, -21)> {};
struct atto : prefix<atto, "a", ratio(1, 1, -18)> {};
struct femto : prefix<femto, "f", ratio(1, 1, -15)> {};
struct pico : prefix<pico, "p", ratio(1, 1, -12)> {};
struct nano : prefix<nano, "n", ratio(1, 1, -9)> {};
struct micro : prefix<micro, basic_symbol_text{"\u00b5", "u"}, ratio(1, 1, -6)> {};
struct milli : prefix<milli, "m", ratio(1, 1, -3)> {};
struct centi : prefix<centi, "c", ratio(1, 1, -2)> {};
struct deci : prefix<deci, "d", ratio(1, 1, -1)> {};
struct deca : prefix<deca, "da", ratio(1, 1, 1)> {};
struct hecto : prefix<hecto, "h", ratio(1, 1, 2)> {};
struct kilo : prefix<kilo, "k", ratio(1, 1, 3)> {};
struct mega : prefix<mega, "M", ratio(1, 1, 6)> {};
struct giga : prefix<giga, "G", ratio(1, 1, 9)> {};
struct tera : prefix<tera, "T", ratio(1, 1, 12)> {};
struct peta : prefix<peta, "P", ratio(1, 1, 15)> {};
struct exa : prefix<exa, "E", ratio(1, 1, 18)> {};
struct zetta : prefix<zetta, "Z", ratio(1, 1, 21)> {};
struct yotta : prefix<yotta, "Y", ratio(1, 1, 24)> {};
struct yocto : prefix<yocto, "y", pow<-24>(as_magnitude<10>())> {};
struct zepto : prefix<zepto, "z", pow<-21>(as_magnitude<10>())> {};
struct atto : prefix<atto, "a", pow<-18>(as_magnitude<10>())> {};
struct femto : prefix<femto, "f", pow<-15>(as_magnitude<10>())> {};
struct pico : prefix<pico, "p", pow<-12>(as_magnitude<10>())> {};
struct nano : prefix<nano, "n", pow<-9>(as_magnitude<10>())> {};
struct micro : prefix<micro, basic_symbol_text{"\u00b5", "u"}, pow<-6>(as_magnitude<10>())> {};
struct milli : prefix<milli, "m", pow<-3>(as_magnitude<10>())> {};
struct centi : prefix<centi, "c", pow<-2>(as_magnitude<10>())> {};
struct deci : prefix<deci, "d", pow<-1>(as_magnitude<10>())> {};
struct deca : prefix<deca, "da", pow<1>(as_magnitude<10>())> {};
struct hecto : prefix<hecto, "h", pow<2>(as_magnitude<10>())> {};
struct kilo : prefix<kilo, "k", pow<3>(as_magnitude<10>())> {};
struct mega : prefix<mega, "M", pow<6>(as_magnitude<10>())> {};
struct giga : prefix<giga, "G", pow<9>(as_magnitude<10>())> {};
struct tera : prefix<tera, "T", pow<12>(as_magnitude<10>())> {};
struct peta : prefix<peta, "P", pow<15>(as_magnitude<10>())> {};
struct exa : prefix<exa, "E", pow<18>(as_magnitude<10>())> {};
struct zetta : prefix<zetta, "Z", pow<21>(as_magnitude<10>())> {};
struct yotta : prefix<yotta, "Y", pow<24>(as_magnitude<10>())> {};
// clang-format on
} // namespace units::isq::si

View File

@@ -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