NTTP ratio support added (resolves #49)

This commit is contained in:
Mateusz Pusz
2020-06-27 19:15:46 +02:00
parent 03facbefd2
commit d171f5451e
47 changed files with 504 additions and 664 deletions

View File

@ -8,6 +8,7 @@
- `math.h` function signatures refactored to use a `Quantity` concept (thanks [@kwikius](https://github.com/kwikius)) - `math.h` function signatures refactored to use a `Quantity` concept (thanks [@kwikius](https://github.com/kwikius))
- FPS system added (thanks [@mikeford3](https://github.com/mikeford3)) - FPS system added (thanks [@mikeford3](https://github.com/mikeford3))
- `quantity_point` support added (thanks [@johelegp](https://github.com/johelegp)) - `quantity_point` support added (thanks [@johelegp](https://github.com/johelegp))
- `ratio` changed to the NTTP kind
- **0.5.0 May 17, 2020** - **0.5.0 May 17, 2020**
- Major refactoring and rewrite of the library - Major refactoring and rewrite of the library

View File

@ -186,20 +186,23 @@ Those units are the scaled versions of a time dimension's base unit,
namely second. Those can be defined easily in the library using namely second. Those can be defined easily in the library using
`named_scaled_unit` class template:: `named_scaled_unit` class template::
struct minute : named_scaled_unit<minute, "min", no_prefix, ratio<60>, second> {}; struct minute : named_scaled_unit<minute, "min", no_prefix, ratio(60), second> {};
struct hour : named_scaled_unit<hour, "h", no_prefix, ratio<60>, minute> {}; struct hour : named_scaled_unit<hour, "h", no_prefix, ratio(60), minute> {};
struct day : named_scaled_unit<hour, "d", no_prefix, ratio<24>, hour> {}; struct day : named_scaled_unit<hour, "d", no_prefix, ratio(24), hour> {};
where `no_prefix` is a special tag type describing that the library should where `no_prefix` is a special tag type describing that the library should
not allow to define a new prefixed unit that would use this unit as a not allow to define a new prefixed unit that would use this unit as a
reference ("kilohours" does not have much sense, right?). The `ratio` type reference ("kilohours" does not have much sense, right?). The `ratio` type
used in the definition is really similar to ``std::ratio`` but it takes used in the definition is really similar to ``std::ratio`` but it takes
the third additional argument that defines the exponent of the ratio. an additional ``Exp`` template parameter that defines the exponent of the ratio.
Another important difference is the fact that the objects of that class are used
as class NTTPs rather then a type template parameter kind.
Thanks to it we can address nearly infinite scaling factors between units Thanks to it we can address nearly infinite scaling factors between units
and define units like:: and define units like::
struct electronvolt : named_scaled_unit<electronvolt, "eV", prefix, struct electronvolt : named_scaled_unit<electronvolt, "eV", prefix,
ratio<1'602'176'634, 1'000'000'000, -19>, joule> {}; ratio(1'602'176'634, 1'000'000'000, -19), joule> {};
.. ..
TODO Submit a bug for above lexing problem TODO Submit a bug for above lexing problem
@ -221,26 +224,26 @@ complete list of all the :term:`SI` prefixes supported by the library::
struct prefix : prefix_family {}; struct prefix : prefix_family {};
struct yocto : units::prefix<yocto, prefix, "y", ratio<1, 1, -24>> {}; struct yocto : units::prefix<yocto, prefix, "y", ratio(1, 1, -24)> {};
struct zepto : units::prefix<zepto, prefix, "z", ratio<1, 1, -21>> {}; struct zepto : units::prefix<zepto, prefix, "z", ratio(1, 1, -21)> {};
struct atto : units::prefix<atto, prefix, "a", ratio<1, 1, -18>> {}; struct atto : units::prefix<atto, prefix, "a", ratio(1, 1, -18)> {};
struct femto : units::prefix<femto, prefix, "f", ratio<1, 1, -15>> {}; struct femto : units::prefix<femto, prefix, "f", ratio(1, 1, -15)> {};
struct pico : units::prefix<pico, prefix, "p", ratio<1, 1, -12>> {}; struct pico : units::prefix<pico, prefix, "p", ratio(1, 1, -12)> {};
struct nano : units::prefix<nano, prefix, "n", ratio<1, 1, -9>> {}; struct nano : units::prefix<nano, prefix, "n", ratio(1, 1, -9)> {};
struct micro : units::prefix<micro, prefix, "µ", ratio<1, 1, -6>> {}; struct micro : units::prefix<micro, prefix, "µ", ratio(1, 1, -6)> {};
struct milli : units::prefix<milli, prefix, "m", ratio<1, 1, -3>> {}; struct milli : units::prefix<milli, prefix, "m", ratio(1, 1, -3)> {};
struct centi : units::prefix<centi, prefix, "c", ratio<1, 1, -2>> {}; struct centi : units::prefix<centi, prefix, "c", ratio(1, 1, -2)> {};
struct deci : units::prefix<deci, prefix, "d", ratio<1, 1, -1>> {}; struct deci : units::prefix<deci, prefix, "d", ratio(1, 1, -1)> {};
struct deca : units::prefix<deca, prefix, "da", ratio<1, 1, 1>> {}; struct deca : units::prefix<deca, prefix, "da", ratio(1, 1, 1)> {};
struct hecto : units::prefix<hecto, prefix, "h", ratio<1, 1, 2>> {}; struct hecto : units::prefix<hecto, prefix, "h", ratio(1, 1, 2)> {};
struct kilo : units::prefix<kilo, prefix, "k", ratio<1, 1, 3>> {}; struct kilo : units::prefix<kilo, prefix, "k", ratio(1, 1, 3)> {};
struct mega : units::prefix<mega, prefix, "M", ratio<1, 1, 6>> {}; struct mega : units::prefix<mega, prefix, "M", ratio(1, 1, 6)> {};
struct giga : units::prefix<giga, prefix, "G", ratio<1, 1, 9>> {}; struct giga : units::prefix<giga, prefix, "G", ratio(1, 1, 9)> {};
struct tera : units::prefix<tera, prefix, "T", ratio<1, 1, 12>> {}; struct tera : units::prefix<tera, prefix, "T", ratio(1, 1, 12)> {};
struct peta : units::prefix<peta, prefix, "P", ratio<1, 1, 15>> {}; struct peta : units::prefix<peta, prefix, "P", ratio(1, 1, 15)> {};
struct exa : units::prefix<exa, prefix, "E", ratio<1, 1, 18>> {}; struct exa : units::prefix<exa, prefix, "E", ratio(1, 1, 18)> {};
struct zetta : units::prefix<zetta, prefix, "Z", ratio<1, 1, 21>> {}; struct zetta : units::prefix<zetta, prefix, "Z", ratio(1, 1, 21)> {};
struct yotta : units::prefix<yotta, prefix, "Y", ratio<1, 1, 24>> {}; struct yotta : units::prefix<yotta, prefix, "Y", ratio(1, 1, 24)> {};
} }
@ -251,12 +254,12 @@ domain::
struct prefix : prefix_family {}; struct prefix : prefix_family {};
struct kibi : units::prefix<kibi, prefix, "Ki", ratio< 1'024>> {}; struct kibi : units::prefix<kibi, prefix, "Ki", ratio( 1'024)> {};
struct mebi : units::prefix<mebi, prefix, "Mi", ratio< 1'048'576>> {}; struct mebi : units::prefix<mebi, prefix, "Mi", ratio( 1'048'576)> {};
struct gibi : units::prefix<gibi, prefix, "Gi", ratio< 1'073'741'824>> {}; struct gibi : units::prefix<gibi, prefix, "Gi", ratio( 1'073'741'824)> {};
struct tebi : units::prefix<tebi, prefix, "Ti", ratio< 1'099'511'627'776>> {}; struct tebi : units::prefix<tebi, prefix, "Ti", ratio( 1'099'511'627'776)> {};
struct pebi : units::prefix<pebi, prefix, "Pi", ratio< 1'125'899'906'842'624>> {}; struct pebi : units::prefix<pebi, prefix, "Pi", ratio( 1'125'899'906'842'624)> {};
struct exbi : units::prefix<exbi, prefix, "Ei", ratio<1'152'921'504'606'846'976>> {}; struct exbi : units::prefix<exbi, prefix, "Ei", ratio(1'152'921'504'606'846'976)> {};
} }
@ -381,7 +384,7 @@ unknown/undefined unit type like in the below example::
Length auto l = 100q_km_per_h * 10q_s; Length auto l = 100q_km_per_h * 10q_s;
The type of ``l`` above will be The type of ``l`` above will be
:expr:`si::length<scaled_unit<ratio<1, 36, 1>, si::metre>, long double>`. This is caused :expr:`si::length<scaled_unit<ratio(1, 36, 1), si::metre>, long double>`. This is caused
by the fact that the library does not define a unit of a length quantity that has the by the fact that the library does not define a unit of a length quantity that has the
ratio ``10/36`` of a `si::metre`. If such a unit was predefined we would see its concrete ratio ``10/36`` of a `si::metre`. If such a unit was predefined we would see its concrete
type here instead. type here instead.

View File

@ -16,14 +16,9 @@ Concepts
A concept matching a symbol prefix. Satisfied by all instantiations of :class:`prefix`. A concept matching a symbol prefix. Satisfied by all instantiations of :class:`prefix`.
.. concept:: template<typename T> Ratio .. concept:: template<ratio R> UnitRatio
A concept matching a ratio. Satisfied by all instantiations of :class:`ratio`. Satisfied by all ratio values for which :expr:`R.num > 0` and :expr:`R.den > 0`.
.. concept:: template<typename R> UnitRatio
A concept matching unit's ratio. Satisfied by all types that satisfy :expr:`Ratio<R>` and
for which :expr:`R::num > 0` and :expr:`R::den > 0`.
.. concept:: template<typename T> BaseDimension .. concept:: template<typename T> BaseDimension

View File

@ -22,7 +22,7 @@ Defining a New Unit
My working desk is of ``180 cm x 60 cm`` which gives an area of ``0.3 m²``. I would like to My working desk is of ``180 cm x 60 cm`` which gives an area of ``0.3 m²``. I would like to
make it a unit of area for my project:: make it a unit of area for my project::
struct desk : named_scaled_unit<desk, "desk", no_prefix, ratio<3, 10>, si::square_metre> {}; struct desk : named_scaled_unit<desk, "desk", no_prefix, ratio(3, 10), si::square_metre> {};
With the above I can define a quantity with the area of ``2 desks``:: With the above I can define a quantity with the area of ``2 desks``::
@ -59,7 +59,7 @@ Enabling a Unit for Prefixing
In case I decide it is reasonable to express my desks with SI prefixes the only thing I have In case I decide it is reasonable to express my desks with SI prefixes the only thing I have
to change in the above code is to replace `no_prefix` with `si_prefix`:: to change in the above code is to replace `no_prefix` with `si_prefix`::
struct desk : named_scaled_unit<desk, "desk", si::prefix, ratio<3, 10>, si::square_metre> {}; struct desk : named_scaled_unit<desk, "desk", si::prefix, ratio(3, 10), si::square_metre> {};
Now I can define a new unit named ``kilodesk``:: Now I can define a new unit named ``kilodesk``::
@ -72,12 +72,12 @@ prefix family and prefixes are needed::
struct shipping_prefix : prefix_family {}; struct shipping_prefix : prefix_family {};
struct package : prefix<package, shipping_prefix, "pkg", ratio<6>> {}; struct package : prefix<package, shipping_prefix, "pkg", ratio(6)> {};
struct lorry : prefix<lorry, shipping_prefix, "lorry", ratio<6 * 40>> {}; struct lorry : prefix<lorry, shipping_prefix, "lorry", ratio(6 * 40)> {};
Now we can use it for our unit:: Now we can use it for our unit::
struct desk : named_scaled_unit<desk, "desk", shipping_prefix, ratio<3, 10>, si::square_metre> {}; struct desk : named_scaled_unit<desk, "desk", shipping_prefix, ratio(3, 10), si::square_metre> {};
struct packagedesk : prefixed_unit<packagedesk, package, desk> {}; struct packagedesk : prefixed_unit<packagedesk, package, desk> {};
struct lorrydesk : prefixed_unit<lorrydesk, lorry, desk> {}; struct lorrydesk : prefixed_unit<lorrydesk, lorry, desk> {};

View File

@ -30,31 +30,22 @@ namespace units::detail {
template<Exponent E> template<Exponent E>
requires (E::den == 1 || E::den == 2) // TODO provide support for any den requires (E::den == 1 || E::den == 2) // TODO provide support for any den
struct exp_ratio { constexpr ratio exp_ratio()
using base_ratio = E::dimension::base_unit::ratio; {
using positive_ratio = conditional<E::num * E::den < 0, ratio<base_ratio::den, base_ratio::num, -base_ratio::exp>, base_ratio>; const ratio base_ratio = E::dimension::base_unit::ratio;
static constexpr std::intmax_t N = E::num * E::den < 0 ? -E::num : E::num; const ratio positive_ratio = E::num * E::den < 0 ? ratio(base_ratio.den, base_ratio.num, -base_ratio.exp) : base_ratio;
using pow = ratio_pow<positive_ratio, N>; const std::intmax_t N = E::num * E::den < 0 ? -E::num : E::num;
using type = conditional<E::den == 2, ratio_sqrt<pow>, pow>; const ratio ratio_pow = pow<N>(positive_ratio);
}; return E::den == 2 ? sqrt(ratio_pow) : ratio_pow;
}
template<typename ExpList>
struct base_units_ratio_impl;
template<typename E, typename... Es>
struct base_units_ratio_impl<exp_list<E, Es...>> {
using type = ratio_multiply<typename exp_ratio<E>::type, typename base_units_ratio_impl<exp_list<Es...>>::type>;
};
template<typename E>
struct base_units_ratio_impl<exp_list<E>> {
using type = exp_ratio<E>::type;
};
/** /**
* @brief Calculates the common ratio of all the references of base units in the derived dimension * @brief Calculates the common ratio of all the references of base units in the derived dimension
*/ */
template<typename D> template<typename... Es>
using base_units_ratio = base_units_ratio_impl<typename D::exponents>::type; constexpr ratio base_units_ratio(exp_list<Es...>)
{
return (exp_ratio<Es>() * ...);
}
} // namespace units::detail } // namespace units::detail

View File

@ -44,20 +44,20 @@ struct common_quantity_impl<quantity<D, U, Rep1>, quantity<D, U, Rep2>, Rep> {
template<typename D, typename U1, typename Rep1, typename U2, typename Rep2, typename Rep> template<typename D, typename U1, typename Rep1, typename U2, typename Rep2, typename Rep>
struct common_quantity_impl<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, Rep> { struct common_quantity_impl<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, Rep> {
using type = quantity<D, downcast_unit<D, common_ratio<typename U1::ratio, typename U2::ratio>>, Rep>; using type = quantity<D, downcast_unit<D, common_ratio(U1::ratio, U2::ratio)>, Rep>;
}; };
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2, typename Rep> template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2, typename Rep>
requires same_unit_reference<dimension_unit<D1>, dimension_unit<D2>>::value requires same_unit_reference<dimension_unit<D1>, dimension_unit<D2>>::value
struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep> { struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep> {
using type = quantity<D1, downcast_unit<D1, common_ratio<typename U1::ratio, typename U2::ratio>>, Rep>; using type = quantity<D1, downcast_unit<D1, common_ratio(U1::ratio, U2::ratio)>, Rep>;
}; };
template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2, typename Rep> template<typename D1, typename U1, typename Rep1, typename D2, typename U2, typename Rep2, typename Rep>
struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep> { struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep> {
using ratio1 = ratio_multiply<typename D1::base_units_ratio, typename U1::ratio>; static constexpr ratio r1 = D1::base_units_ratio * U1::ratio;
using ratio2 = ratio_multiply<typename D2::base_units_ratio, typename U2::ratio>; static constexpr ratio r2 = D2::base_units_ratio * U2::ratio;
using type = quantity<D1, downcast_unit<D1, common_ratio<ratio1, ratio2>>, Rep>; using type = quantity<D1, downcast_unit<D1, common_ratio(r1, r2)>, Rep>;
}; };
template<typename D, typename U, typename Rep> template<typename D, typename U, typename Rep>

View File

@ -34,39 +34,23 @@ template<typename... Es, Unit... Us>
inline constexpr bool same_scaled_units<exp_list<Es...>, Us...> = (UnitOf<Us, typename Es::dimension> && ...); inline constexpr bool same_scaled_units<exp_list<Es...>, Us...> = (UnitOf<Us, typename Es::dimension> && ...);
// deduced_unit // deduced_unit
template<typename Result, int UnitExpNum, int UnitExpDen, typename UnitRatio>
struct ratio_op;
template<typename Result, int UnitExpDen, typename UnitRatio> template<Exponent E>
struct ratio_op<Result, 0, UnitExpDen, UnitRatio> { constexpr ratio inverse_if_negative(const ratio& r)
using ratio = Result; {
}; if constexpr(E::num * E::den > 0)
return r;
else
return inverse(r);
}
template<typename Result, int UnitExpNum, int UnitExpDen, typename UnitRatio> template<Unit... Us, typename... Es>
struct ratio_op { constexpr ratio derived_ratio(exp_list<Es...>)
using calc_ratio = {
conditional<(UnitExpNum * UnitExpDen > 0), ratio_multiply<Result, UnitRatio>, ratio_divide<Result, UnitRatio>>; return (... * inverse_if_negative<Es>(pow<detail::abs(Es::num)>(Us::ratio) / dimension_unit<typename Es::dimension>::ratio));
static constexpr int value = (UnitExpNum * UnitExpDen > 0) ? (UnitExpNum - UnitExpDen) : (UnitExpNum + UnitExpDen); }
using ratio = ratio_op<calc_ratio, value, UnitExpDen, UnitRatio>::ratio;
};
template<typename ExpList, Unit... Us>
struct derived_ratio;
template<Unit... Us>
struct derived_ratio<exp_list<>, Us...> {
using ratio = ::units::ratio<1>;
};
template<typename E, typename... ERest, Unit U, Unit... URest>
struct derived_ratio<exp_list<E, ERest...>, U, URest...> {
using rest_ratio = derived_ratio<exp_list<ERest...>, URest...>::ratio;
using unit_ratio = ratio_op<rest_ratio, E::num, E::den, typename U::ratio>::ratio;
using ratio = ratio_divide<unit_ratio, typename dimension_unit<typename E::dimension>::ratio>;
};
template<DerivedDimension D, Unit... Us> template<DerivedDimension D, Unit... Us>
using deduced_unit = using deduced_unit = scaled_unit<derived_ratio<Us...>(typename D::recipe()), typename D::coherent_unit::reference>;
scaled_unit<typename detail::derived_ratio<typename D::recipe, Us...>::ratio, typename D::coherent_unit::reference>;
} // namespace units::detail } // namespace units::detail

View File

@ -66,14 +66,14 @@ inline constexpr bool equivalent_dim = detail::equivalent_dim_impl<D1, D2>::valu
* *
* Sometimes a temporary partial result of a complex calculation may not result in a predefined * Sometimes a temporary partial result of a complex calculation may not result in a predefined
* dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_coherent_unit` * dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_coherent_unit`
* and ratio<1>. * and ratio(1).
* *
* @tparam E the list of exponents of ingredient dimensions * @tparam E the list of exponents of ingredient dimensions
* @tparam ERest the list of exponents of ingredient dimensions * @tparam ERest the list of exponents of ingredient dimensions
*/ */
template<Exponent E, Exponent... ERest> template<Exponent E, Exponent... ERest>
struct unknown_dimension : derived_dimension<unknown_dimension<E, ERest...>, scaled_unit<ratio<1>, unknown_coherent_unit>, E, ERest...> { struct unknown_dimension : derived_dimension<unknown_dimension<E, ERest...>, scaled_unit<ratio(1), unknown_coherent_unit>, E, ERest...> {
using coherent_unit = scaled_unit<ratio<1>, unknown_coherent_unit>; using coherent_unit = scaled_unit<ratio(1), unknown_coherent_unit>;
}; };
namespace detail { namespace detail {

View File

@ -23,7 +23,6 @@
#pragma once #pragma once
#include <units/bits/external/hacks.h> #include <units/bits/external/hacks.h>
#include <units/concepts.h>
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
@ -46,7 +45,7 @@ template<typename T>
// Computes (a * b) mod m relies on unsigned integer arithmetic, should not // Computes (a * b) mod m relies on unsigned integer arithmetic, should not
// overflow // overflow
constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m) [[nodiscard]] constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m)
{ {
std::uint64_t res = 0; std::uint64_t res = 0;
@ -78,7 +77,7 @@ constexpr std::uint64_t mulmod(std::uint64_t a, std::uint64_t b, std::uint64_t m
} }
// Calculates (a ^ e) mod m , should not overflow. // Calculates (a ^ e) mod m , should not overflow.
constexpr std::uint64_t modpow(std::uint64_t a, std::uint64_t e, std::uint64_t m) [[nodiscard]] constexpr std::uint64_t modpow(std::uint64_t a, std::uint64_t e, std::uint64_t m)
{ {
a %= m; a %= m;
std::uint64_t result = 1; std::uint64_t result = 1;
@ -94,7 +93,7 @@ constexpr std::uint64_t modpow(std::uint64_t a, std::uint64_t e, std::uint64_t m
} }
// gcd(a * 10 ^ e, b), should not overflow // gcd(a * 10 ^ e, b), should not overflow
constexpr std::intmax_t gcdpow(std::intmax_t a, std::intmax_t e, std::intmax_t b) noexcept [[nodiscard]] constexpr std::intmax_t gcdpow(std::intmax_t a, std::intmax_t e, std::intmax_t b) noexcept
{ {
assert(a > 0); assert(a > 0);
assert(e >= 0); assert(e >= 0);
@ -120,8 +119,8 @@ constexpr void cwap(std::intmax_t& lhs, std::intmax_t& rhs)
} }
// Computes the rational gcd of n1/d1 x 10^e1 and n2/d2 x 10^e2 // Computes the rational gcd of n1/d1 x 10^e1 and n2/d2 x 10^e2
constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t e1, std::intmax_t n2, std::intmax_t d2, [[nodiscard]] constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t e1, std::intmax_t n2, std::intmax_t d2,
std::intmax_t e2) noexcept std::intmax_t e2) noexcept
{ {
// Short cut for equal ratios // Short cut for equal ratios
if (n1 == n2 && d1 == d2 && e1 == e2) { if (n1 == n2 && d1 == d2 && e1 == e2) {
@ -152,8 +151,14 @@ constexpr auto gcd_frac(std::intmax_t n1, std::intmax_t d1, std::intmax_t e1, st
return std::array{num / gcd, den / gcd, exp}; return std::array{num / gcd, den / gcd, exp};
} }
constexpr auto normalize(std::intmax_t num, std::intmax_t den, std::intmax_t exp) constexpr void normalize(std::intmax_t& num, std::intmax_t& den, std::intmax_t& exp)
{ {
if(num == 0) {
den = 1;
exp = 0;
return;
}
std::intmax_t gcd = std::gcd(num, den); std::intmax_t gcd = std::gcd(num, den);
num = num * (den < 0 ? -1 : 1) / gcd; num = num * (den < 0 ? -1 : 1) / gcd;
den = detail::abs(den) / gcd; den = detail::abs(den) / gcd;
@ -166,8 +171,23 @@ constexpr auto normalize(std::intmax_t num, std::intmax_t den, std::intmax_t exp
den /= 10; den /= 10;
--exp; --exp;
} }
}
return std::array{num, den, exp}; [[nodiscard]] static constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs)
{
constexpr std::intmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4);
const std::intmax_t a0 = detail::abs(lhs) % c;
const std::intmax_t a1 = detail::abs(lhs) / c;
const std::intmax_t b0 = detail::abs(rhs) % c;
const std::intmax_t b1 = detail::abs(rhs) / c;
Expects(a1 == 0 || b1 == 0); // overflow in multiplication
Expects(a0 * b1 + b0 * a1 < (c >> 1)); // overflow in multiplication
Expects(b0 * a0 <= INTMAX_MAX); // overflow in multiplication
Expects((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); // overflow in multiplication
return lhs * rhs;
} }
} // namespace units::detail } // namespace units::detail

View File

@ -32,31 +32,31 @@ namespace units::detail {
inline constexpr basic_symbol_text base_multiplier("\u00D7 10", "x 10"); inline constexpr basic_symbol_text base_multiplier("\u00D7 10", "x 10");
template<typename Ratio> template<ratio R>
constexpr auto ratio_text() constexpr auto ratio_text()
{ {
if constexpr(Ratio::num == 1 && Ratio::den == 1 && Ratio::exp != 0) { if constexpr(R.num == 1 && R.den == 1 && R.exp != 0) {
return base_multiplier + superscript<Ratio::exp>() + basic_fixed_string(" "); return base_multiplier + superscript<R.exp>() + basic_fixed_string(" ");
} }
else if constexpr(Ratio::num != 1 || Ratio::den != 1 || Ratio::exp != 0) { else if constexpr(R.num != 1 || R.den != 1 || R.exp != 0) {
auto txt = basic_fixed_string("[") + regular<Ratio::num>(); auto txt = basic_fixed_string("[") + regular<R.num>();
if constexpr(Ratio::den == 1) { if constexpr(R.den == 1) {
if constexpr(Ratio::exp == 0) { if constexpr(R.exp == 0) {
return txt + basic_fixed_string("] "); return txt + basic_fixed_string("] ");
} }
else { else {
return txt + " " + base_multiplier + superscript<Ratio::exp>() + return txt + " " + base_multiplier + superscript<R.exp>() +
basic_fixed_string("] "); basic_fixed_string("] ");
} }
} }
else { else {
if constexpr(Ratio::exp == 0) { if constexpr(R.exp == 0) {
return txt + basic_fixed_string("/") + regular<Ratio::den>() + return txt + basic_fixed_string("/") + regular<R.den>() +
basic_fixed_string("] "); basic_fixed_string("] ");
} }
else { else {
return txt + basic_fixed_string("/") + regular<Ratio::den>() + return txt + basic_fixed_string("/") + regular<R.den>() +
" " + base_multiplier + superscript<Ratio::exp>() + " " + base_multiplier + superscript<R.exp>() +
basic_fixed_string("] "); basic_fixed_string("] ");
} }
} }
@ -66,30 +66,30 @@ constexpr auto ratio_text()
} }
} }
template<typename Ratio, typename PrefixFamily> template<ratio R, typename PrefixFamily>
constexpr auto prefix_or_ratio_text() constexpr auto prefix_or_ratio_text()
{ {
if constexpr(Ratio::num == 1 && Ratio::den == 1 && Ratio::exp == 0) { if constexpr(R.num == 1 && R.den == 1 && R.exp == 0) {
// no ratio/prefix // no ratio/prefix
return basic_fixed_string(""); return basic_fixed_string("");
} }
else { else {
if constexpr (!std::is_same_v<PrefixFamily, no_prefix>) { if constexpr (!std::is_same_v<PrefixFamily, no_prefix>) {
// try to form a prefix // try to form a prefix
using prefix = downcast<detail::prefix_base<PrefixFamily, Ratio>>; using prefix = downcast<detail::prefix_base<PrefixFamily, R>>;
if constexpr(!std::is_same_v<prefix, prefix_base<PrefixFamily, Ratio>>) { if constexpr(!std::is_same_v<prefix, prefix_base<PrefixFamily, R>>) {
// print as a prefixed unit // print as a prefixed unit
return prefix::symbol; return prefix::symbol;
} }
else { else {
// print as a ratio of the coherent unit // print as a ratio of the coherent unit
return ratio_text<Ratio>(); return ratio_text<R>();
} }
} }
else { else {
// print as a ratio of the coherent unit // print as a ratio of the coherent unit
return ratio_text<Ratio>(); return ratio_text<R>();
} }
} }
} }
@ -150,8 +150,7 @@ constexpr auto unit_text()
else { else {
// print as a prefix or ratio of a coherent unit // print as a prefix or ratio of a coherent unit
using coherent_unit = dimension_unit<Dim>; using coherent_unit = dimension_unit<Dim>;
using ratio = ratio_divide<typename U::ratio, typename coherent_unit::ratio>; auto prefix_txt = prefix_or_ratio_text<U::ratio / coherent_unit::ratio, typename U::reference::prefix_family>();
auto prefix_txt = prefix_or_ratio_text<ratio, typename U::reference::prefix_family>();
if constexpr(has_symbol<coherent_unit>) { if constexpr(has_symbol<coherent_unit>) {
// use predefined coherent unit symbol // use predefined coherent unit symbol

View File

@ -25,6 +25,7 @@
#include <units/bits/external/downcasting.h> #include <units/bits/external/downcasting.h>
#include <units/bits/external/fixed_string.h> #include <units/bits/external/fixed_string.h>
#include <units/bits/external/hacks.h> #include <units/bits/external/hacks.h>
#include <units/ratio.h>
#include <units/bits/external/type_traits.h> #include <units/bits/external/type_traits.h>
namespace units { namespace units {
@ -63,40 +64,40 @@ template<typename T>
concept Prefix = true; concept Prefix = true;
namespace detail {
template<typename T>
inline constexpr bool is_ratio = false;
} // namespace detail
/**
* @brief A concept matching a ratio
*
* Satisfied by all instantiations of `ratio`.
*/
template<typename T>
concept Ratio = detail::is_ratio<T>;
/** /**
* @brief A concept matching unit's ratio * @brief A concept matching unit's ratio
* *
* Satisfied by all types that satisfy `Ratio<R>` and for which `R::num > 0` and `R::den > 0` * Satisfied by all ratio values for which `R.num > 0` and `R.den > 0`.
*/ */
template<typename R> template<ratio R>
concept UnitRatio = Ratio<R> && R::num > 0 && R::den > 0; // double negatives not allowed concept UnitRatio = R.num > 0 && R.den > 0;
// Unit // Unit
template<UnitRatio R, typename U> template<ratio R, typename U>
requires UnitRatio<R>
struct scaled_unit; struct scaled_unit;
// TODO: Remove when P1985 accepted
namespace detail {
struct is_derived_from_scaled_unit_impl {
template<ratio R, typename U>
static constexpr std::true_type check_base(const scaled_unit<R, U>&);
static constexpr std::false_type check_base(...);
};
template<typename T>
inline constexpr bool is_derived_from_scaled_unit = decltype(is_derived_from_scaled_unit_impl::check_base(std::declval<T>()))::value;
} // namespace detail
/** /**
* @brief A concept matching all unit types in the library * @brief A concept matching all unit types in the library
* *
* Satisfied by all unit types derived from the instantiation of :class:`scaled_unit`. * Satisfied by all unit types derived from the instantiation of :class:`scaled_unit`.
*/ */
template<typename T> template<typename T>
concept Unit = is_derived_from_instantiation<T, scaled_unit>; concept Unit = detail::is_derived_from_scaled_unit<T>;
template<basic_fixed_string Symbol, Unit U> template<basic_fixed_string Symbol, Unit U>
requires U::is_named requires U::is_named

View File

@ -36,7 +36,7 @@ struct gibibit : prefixed_unit<gibibit, gibi, bit> {};
struct tebibit : prefixed_unit<tebibit, tebi, bit> {}; struct tebibit : prefixed_unit<tebibit, tebi, bit> {};
struct pebibit : prefixed_unit<pebibit, pebi, bit> {}; struct pebibit : prefixed_unit<pebibit, pebi, bit> {};
struct byte : named_scaled_unit<byte, "B", prefix, ratio<8>, bit> {}; struct byte : named_scaled_unit<byte, "B", prefix, ratio(8), bit> {};
struct kibibyte : prefixed_unit<kibibyte, kibi, byte> {}; struct kibibyte : prefixed_unit<kibibyte, kibi, byte> {};
struct mebibyte : prefixed_unit<mebibyte, mebi, byte> {}; struct mebibyte : prefixed_unit<mebibyte, mebi, byte> {};
struct gibibyte : prefixed_unit<gibibyte, gibi, byte> {}; struct gibibyte : prefixed_unit<gibibyte, gibi, byte> {};

View File

@ -28,11 +28,11 @@ namespace units::data {
struct prefix : prefix_family {}; struct prefix : prefix_family {};
struct kibi : units::prefix<kibi, prefix, "Ki", ratio< 1'024>> {}; struct kibi : units::prefix<kibi, prefix, "Ki", ratio( 1'024)> {};
struct mebi : units::prefix<mebi, prefix, "Mi", ratio< 1'048'576>> {}; struct mebi : units::prefix<mebi, prefix, "Mi", ratio( 1'048'576)> {};
struct gibi : units::prefix<gibi, prefix, "Gi", ratio< 1'073'741'824>> {}; struct gibi : units::prefix<gibi, prefix, "Gi", ratio( 1'073'741'824)> {};
struct tebi : units::prefix<tebi, prefix, "Ti", ratio< 1'099'511'627'776>> {}; struct tebi : units::prefix<tebi, prefix, "Ti", ratio( 1'099'511'627'776)> {};
struct pebi : units::prefix<pebi, prefix, "Pi", ratio< 1'125'899'906'842'624>> {}; struct pebi : units::prefix<pebi, prefix, "Pi", ratio( 1'125'899'906'842'624)> {};
struct exbi : units::prefix<exbi, prefix, "Ei", ratio<1'152'921'504'606'846'976>> {}; struct exbi : units::prefix<exbi, prefix, "Ei", ratio(1'152'921'504'606'846'976)> {};
} // namespace units::data } // namespace units::data

View File

@ -82,7 +82,7 @@ template<typename Child, Unit U, Exponent E, Exponent... ERest>
struct derived_dimension : downcast_child<Child, typename detail::make_dimension<E, ERest...>> { struct derived_dimension : downcast_child<Child, typename detail::make_dimension<E, ERest...>> {
using recipe = exp_list<E, ERest...>; using recipe = exp_list<E, ERest...>;
using coherent_unit = U; using coherent_unit = U;
using base_units_ratio = detail::base_units_ratio<derived_dimension>; static constexpr ratio base_units_ratio = detail::base_units_ratio(typename derived_dimension::exponents());
}; };
} // namespace units } // namespace units

View File

@ -70,10 +70,8 @@ namespace detail {
template<Exponent E, std::intmax_t Num, std::intmax_t Den> template<Exponent E, std::intmax_t Num, std::intmax_t Den>
struct exp_multiply_impl { struct exp_multiply_impl {
using r1 = ratio<E::num, E::den>; static constexpr ratio r = ratio(E::num, E::den) * ratio(Num, Den);
using r2 = ratio<Num, Den>; using type = exp<typename E::dimension, r.num, r.den>;
using r = ratio_multiply<r1, r2>;
using type = exp<typename E::dimension, r::num, r::den>;
}; };
} // namespace detail } // namespace detail

View File

@ -44,8 +44,7 @@ inline Quantity AUTO pow(const Q& q) noexcept
requires requires { std::pow(q.count(), N); } requires requires { std::pow(q.count(), N); }
{ {
using dim = dimension_pow<typename Q::dimension, N>; using dim = dimension_pow<typename Q::dimension, N>;
using ratio = ratio_pow<typename Q::unit::ratio, N>; using unit = downcast_unit<dim, pow<N>(Q::unit::ratio)>;
using unit = downcast_unit<dim, ratio>;
using rep = Q::rep; using rep = Q::rep;
return quantity<dim, unit, rep>(static_cast<rep>(std::pow(q.count(), N))); return quantity<dim, unit, rep>(static_cast<rep>(std::pow(q.count(), N)));
} }
@ -75,8 +74,7 @@ inline Quantity AUTO sqrt(const Q& q) noexcept
requires requires { std::sqrt(q.count()); } requires requires { std::sqrt(q.count()); }
{ {
using dim = dimension_sqrt<typename Q::dimension>; using dim = dimension_sqrt<typename Q::dimension>;
using ratio = ratio_sqrt<typename Q::unit::ratio>; using unit = downcast_unit<dim, sqrt(Q::unit::ratio)>;
using unit = downcast_unit<dim, ratio>;
using rep = Q::rep; using rep = Q::rep;
return quantity<dim, unit, rep>(static_cast<rep>(std::sqrt(q.count()))); return quantity<dim, unit, rep>(static_cast<rep>(std::sqrt(q.count())));
} }

View File

@ -34,7 +34,7 @@ namespace units::physical::fps {
struct poundal : named_unit<poundal, "pdl", no_prefix> {}; struct poundal : named_unit<poundal, "pdl", no_prefix> {};
// https://en.wikipedia.org/wiki/Pound_(force) // https://en.wikipedia.org/wiki/Pound_(force)
struct pound_force : named_scaled_unit<pound_force, "lbf", si::prefix, ratio<32'174'049, 1'000'000>, poundal> {}; struct pound_force : named_scaled_unit<pound_force, "lbf", si::prefix, ratio(32'174'049, 1'000'000), poundal> {};
struct kilopound_force : prefixed_unit<kilopound_force, si::kilo, pound_force> {}; struct kilopound_force : prefixed_unit<kilopound_force, si::kilo, pound_force> {};

View File

@ -29,24 +29,24 @@
namespace units::physical::fps { namespace units::physical::fps {
// https://en.wikipedia.org/wiki/Foot_(unit) // https://en.wikipedia.org/wiki/Foot_(unit)
struct foot : named_scaled_unit<foot, "ft", no_prefix, ratio<3'048, 1'000, -1>, si::metre> {}; struct foot : named_scaled_unit<foot, "ft", no_prefix, ratio(3'048, 1'000, -1), si::metre> {};
struct inch : named_scaled_unit<inch, "in", no_prefix, ratio<1, 12>, foot> {}; struct inch : named_scaled_unit<inch, "in", no_prefix, ratio(1, 12), foot> {};
// thousandth of an inch // thousandth of an inch
struct thousandth : named_scaled_unit<thousandth, "thou", no_prefix, ratio<1, 1'000>, inch> {}; struct thousandth : named_scaled_unit<thousandth, "thou", no_prefix, ratio(1, 1'000), inch> {};
struct thou : alias_unit<thousandth, "thou", no_prefix>{}; struct thou : alias_unit<thousandth, "thou", no_prefix>{};
struct mil : alias_unit<thousandth, "mil", no_prefix>{}; struct mil : alias_unit<thousandth, "mil", no_prefix>{};
struct yard : named_scaled_unit<yard, "yd", si::prefix, ratio<3, 1>, foot> {}; struct yard : named_scaled_unit<yard, "yd", si::prefix, ratio(3, 1), foot> {};
struct fathom : named_scaled_unit<fathom, "ftm", no_prefix, ratio<6, 1>, foot> {}; struct fathom : named_scaled_unit<fathom, "ftm", no_prefix, ratio(6, 1), foot> {};
struct kiloyard : prefixed_unit<kiloyard, si::kilo, yard> {}; struct kiloyard : prefixed_unit<kiloyard, si::kilo, yard> {};
struct mile : named_scaled_unit<mile, "mile", no_prefix, ratio<5'280>, foot> {}; struct mile : named_scaled_unit<mile, "mile", no_prefix, ratio(5'280), foot> {};
struct nautical_mile : named_scaled_unit<nautical_mile, "mi(naut)", no_prefix, ratio<2'000>, yard> {}; struct nautical_mile : named_scaled_unit<nautical_mile, "mi(naut)", no_prefix, ratio(2'000), yard> {};

View File

@ -29,28 +29,28 @@
namespace units::physical::fps { namespace units::physical::fps {
// https://en.wikipedia.org/wiki/Pound_(mass) // https://en.wikipedia.org/wiki/Pound_(mass)
struct pound : named_scaled_unit<pound, "lb", no_prefix, ratio<45'359'237, 100'000'000>, si::kilogram> {}; struct pound : named_scaled_unit<pound, "lb", no_prefix, ratio(45'359'237, 100'000'000), si::kilogram> {};
struct dim_mass : physical::dim_mass<pound> {}; struct dim_mass : physical::dim_mass<pound> {};
template<Unit U, Scalar Rep = double> template<Unit U, Scalar Rep = double>
using mass = quantity<dim_mass, U, Rep>; using mass = quantity<dim_mass, U, Rep>;
struct grain : named_scaled_unit<grain, "gr", no_prefix, ratio<1, 7000>, pound>{}; struct grain : named_scaled_unit<grain, "gr", no_prefix, ratio(1, 7000), pound>{};
struct dram : named_scaled_unit<dram, "dr", no_prefix, ratio<1, 256>, pound>{}; struct dram : named_scaled_unit<dram, "dr", no_prefix, ratio(1, 256), pound>{};
struct ounce : named_scaled_unit<ounce, "oz", no_prefix, ratio<1, 16>, pound>{}; struct ounce : named_scaled_unit<ounce, "oz", no_prefix, ratio(1, 16), pound>{};
struct stone : named_scaled_unit<stone, "st", no_prefix, ratio<14, 1>, pound>{}; struct stone : named_scaled_unit<stone, "st", no_prefix, ratio(14, 1), pound>{};
struct quarter : named_scaled_unit<quarter, "qr", no_prefix, ratio<28, 1>, pound>{}; struct quarter : named_scaled_unit<quarter, "qr", no_prefix, ratio(28, 1), pound>{};
struct hundredweight : named_scaled_unit<hundredweight, "cwt", no_prefix, ratio<112, 1>, pound>{}; struct hundredweight : named_scaled_unit<hundredweight, "cwt", no_prefix, ratio(112, 1), pound>{};
struct short_ton : named_scaled_unit<short_ton, "ton (short)", no_prefix, ratio<2'000, 1>, pound>{}; struct short_ton : named_scaled_unit<short_ton, "ton (short)", no_prefix, ratio(2'000, 1), pound>{};
struct long_ton : named_scaled_unit<long_ton, "ton (long)", no_prefix, ratio<2'240, 1>, pound>{}; struct long_ton : named_scaled_unit<long_ton, "ton (long)", no_prefix, ratio(2'240, 1), pound>{};
inline namespace literals { inline namespace literals {

View File

@ -35,7 +35,7 @@ struct dim_power : physical::dim_power<dim_power, foot_poundal_per_second, dim_e
struct foot_pound_force_per_second : deduced_unit<foot_pound_force_per_second, dim_power, foot_pound_force, second> {}; struct foot_pound_force_per_second : deduced_unit<foot_pound_force_per_second, dim_power, foot_pound_force, second> {};
struct horse_power : named_scaled_unit<horse_power, "hp", no_prefix, ratio<550>, foot_pound_force_per_second> {}; struct horse_power : named_scaled_unit<horse_power, "hp", no_prefix, ratio(550), foot_pound_force_per_second> {};
template<Unit U, Scalar Rep = double> template<Unit U, Scalar Rep = double>
using power = quantity<dim_power, U, Rep>; using power = quantity<dim_power, U, Rep>;

View File

@ -37,9 +37,9 @@ struct dim_pressure : physical::dim_pressure<dim_pressure, poundal_per_foot_sq,
template<Unit U, Scalar Rep = double> template<Unit U, Scalar Rep = double>
using pressure = quantity<dim_pressure, U, Rep>; using pressure = quantity<dim_pressure, U, Rep>;
struct pound_force_per_foot_sq : named_scaled_unit<pound_force_per_foot_sq, "lbf ft2", si::prefix, ratio<32'174'049, 1'000'000>, poundal_per_foot_sq> {}; struct pound_force_per_foot_sq : named_scaled_unit<pound_force_per_foot_sq, "lbf ft2", si::prefix, ratio(32'174'049, 1'000'000), poundal_per_foot_sq> {};
struct pound_force_per_inch_sq : named_scaled_unit<pound_force_per_inch_sq, "psi", si::prefix, ratio<1, 144>, pound_force_per_foot_sq> {}; struct pound_force_per_inch_sq : named_scaled_unit<pound_force_per_inch_sq, "psi", si::prefix, ratio(1, 144), pound_force_per_foot_sq> {};
struct kilopound_force_per_inch_sq : prefixed_unit<kilopound_force_per_inch_sq, si::kilo, pound_force_per_inch_sq> {}; struct kilopound_force_per_inch_sq : prefixed_unit<kilopound_force_per_inch_sq, si::kilo, pound_force_per_inch_sq> {};

View File

@ -28,13 +28,13 @@
namespace units::physical::iau { namespace units::physical::iau {
// https://en.wikipedia.org/wiki/Light-year // https://en.wikipedia.org/wiki/Light-year
struct light_year : named_scaled_unit<light_year, "ly", no_prefix, ratio<9460730472580800>, si::metre> {}; struct light_year : named_scaled_unit<light_year, "ly", no_prefix, ratio(9460730472580800), si::metre> {};
// https://en.wikipedia.org/wiki/Parsec // https://en.wikipedia.org/wiki/Parsec
struct parsec : named_scaled_unit<parsec, "pc", si::prefix, ratio<30'856'775'814'913'673>, si::metre> {}; struct parsec : named_scaled_unit<parsec, "pc", si::prefix, ratio(30'856'775'814'913'673), si::metre> {};
// https://en.wikipedia.org/wiki/Angstrom // https://en.wikipedia.org/wiki/Angstrom
struct angstrom : named_scaled_unit<angstrom, "angstrom", no_prefix, ratio<1, 1, -10>, si::metre> {}; struct angstrom : named_scaled_unit<angstrom, "angstrom", no_prefix, ratio(1, 1, -10), si::metre> {};
inline namespace literals { inline namespace literals {

View File

@ -27,10 +27,10 @@
namespace units::physical::imperial { namespace units::physical::imperial {
// https://en.wikipedia.org/wiki/Chain_(unit) // https://en.wikipedia.org/wiki/Chain_(unit)
struct chain : named_scaled_unit<chain, "ch", no_prefix, ratio<22, 1>, international::yard> {}; struct chain : named_scaled_unit<chain, "ch", no_prefix, ratio(22, 1), international::yard> {};
// https://en.wikipedia.org/wiki/Rod_(unit) // https://en.wikipedia.org/wiki/Rod_(unit)
struct rod : named_scaled_unit<rod, "rd", no_prefix, ratio<1, 4>, chain> {}; struct rod : named_scaled_unit<rod, "rd", no_prefix, ratio(1, 4), chain> {};
inline namespace literals { inline namespace literals {

View File

@ -29,30 +29,30 @@ namespace units::physical::international {
// international yard // international yard
// https://en.wikipedia.org/wiki/International_yard_and_pound // https://en.wikipedia.org/wiki/International_yard_and_pound
struct yard : named_scaled_unit<yard, "yd", no_prefix, ratio<9'144, 1'000, -1>, si::metre> {}; struct yard : named_scaled_unit<yard, "yd", no_prefix, ratio(9'144, 1'000, -1), si::metre> {};
// international foot // international foot
// https://en.wikipedia.org/wiki/Foot_(unit)#International_foot // https://en.wikipedia.org/wiki/Foot_(unit)#International_foot
struct foot : named_scaled_unit<foot, "ft", no_prefix, ratio<1, 3>, yard> {}; struct foot : named_scaled_unit<foot, "ft", no_prefix, ratio(1, 3), yard> {};
// https://en.wikipedia.org/wiki/Fathom#International_fathom // https://en.wikipedia.org/wiki/Fathom#International_fathom
struct fathom : named_scaled_unit<fathom, "fathom", no_prefix, ratio<2>, yard> {}; struct fathom : named_scaled_unit<fathom, "fathom", no_prefix, ratio(2), yard> {};
// international inch // international inch
// https://en.wikipedia.org/wiki/Inch#Equivalences // https://en.wikipedia.org/wiki/Inch#Equivalences
struct inch : named_scaled_unit<inch, "in", no_prefix, ratio<1, 36>, yard> {}; struct inch : named_scaled_unit<inch, "in", no_prefix, ratio(1, 36), yard> {};
// intrnational mile // intrnational mile
// https://en.wikipedia.org/wiki/Mile#International_mile // https://en.wikipedia.org/wiki/Mile#International_mile
struct mile : named_scaled_unit<mile, "mi", no_prefix, ratio<25'146, 15'625>, si::kilometre> {}; struct mile : named_scaled_unit<mile, "mi", no_prefix, ratio(25'146, 15'625), si::kilometre> {};
// international nautical mile // international nautical mile
// https://en.wikipedia.org/wiki/Nautical_mile // https://en.wikipedia.org/wiki/Nautical_mile
struct nautical_mile : named_scaled_unit<nautical_mile, "mi(naut)", no_prefix, ratio<1852>, si::metre> {}; struct nautical_mile : named_scaled_unit<nautical_mile, "mi(naut)", no_prefix, ratio(1852), si::metre> {};
// thou // thou
// https://en.wikipedia.org/wiki/Thousandth_of_an_inch // https://en.wikipedia.org/wiki/Thousandth_of_an_inch
struct thou : named_scaled_unit<thou, "thou", no_prefix, ratio<1, 1000>, inch> {}; struct thou : named_scaled_unit<thou, "thou", no_prefix, ratio(1, 1000), inch> {};
// mil - different name for thou // mil - different name for thou
// https://en.wikipedia.org/wiki/Thousandth_of_an_inch // https://en.wikipedia.org/wiki/Thousandth_of_an_inch

View File

@ -52,7 +52,7 @@ struct exakatal : prefixed_unit<exakatal, exa, katal> {};
struct zettakatal : prefixed_unit<zettakatal, zetta, katal> {}; struct zettakatal : prefixed_unit<zettakatal, zetta, katal> {};
struct yottakatal : prefixed_unit<yottakatal, yotta, katal> {}; struct yottakatal : prefixed_unit<yottakatal, yotta, katal> {};
struct enzyme_unit : named_scaled_unit<enzyme_unit, "U", prefix, ratio<1, 60, -6>, katal> {}; struct enzyme_unit : named_scaled_unit<enzyme_unit, "U", prefix, ratio(1, 60, -6), katal> {};
struct dim_catalytic_activity : physical::dim_catalytic_activity<dim_catalytic_activity, katal, dim_time, dim_substance> {}; struct dim_catalytic_activity : physical::dim_catalytic_activity<dim_catalytic_activity, katal, dim_time, dim_substance> {};

View File

@ -47,7 +47,7 @@ struct exajoule : prefixed_unit<exajoule, exa, joule> {};
struct zettajoule : prefixed_unit<zettajoule, zetta, joule> {}; struct zettajoule : prefixed_unit<zettajoule, zetta, joule> {};
struct yottajoule : prefixed_unit<yottajoule, yotta, joule> {}; struct yottajoule : prefixed_unit<yottajoule, yotta, joule> {};
struct electronvolt : named_scaled_unit<electronvolt, "eV", prefix, ratio<1'602'176'634, 1'000'000'000, -19>, joule> {}; struct electronvolt : named_scaled_unit<electronvolt, "eV", prefix, ratio(1'602'176'634, 1'000'000'000, -19), joule> {};
struct gigaelectronvolt : prefixed_unit<gigaelectronvolt, giga, electronvolt> {}; struct gigaelectronvolt : prefixed_unit<gigaelectronvolt, giga, electronvolt> {};
struct dim_energy : physical::dim_energy<dim_energy, joule, dim_force, dim_length> {}; struct dim_energy : physical::dim_energy<dim_energy, joule, dim_force, dim_length> {};

View File

@ -50,7 +50,7 @@ struct exametre : prefixed_unit<exametre, exa, metre> {};
struct zettametre : prefixed_unit<zettametre, zetta, metre> {}; struct zettametre : prefixed_unit<zettametre, zetta, metre> {};
struct yottametre : prefixed_unit<yottametre, yotta, metre> {}; struct yottametre : prefixed_unit<yottametre, yotta, metre> {};
struct astronomical_unit : named_scaled_unit<astronomical_unit, "au", no_prefix, ratio<149'597'870'700>, metre> {}; struct astronomical_unit : named_scaled_unit<astronomical_unit, "au", no_prefix, ratio(149'597'870'700), metre> {};
struct dim_length : physical::dim_length<metre> {}; struct dim_length : physical::dim_length<metre> {};

View File

@ -50,7 +50,7 @@ struct exatesla : prefixed_unit<exatesla, exa, tesla> {};
struct zettatesla : prefixed_unit<zettatesla, zetta, tesla> {}; struct zettatesla : prefixed_unit<zettatesla, zetta, tesla> {};
struct yottatesla : prefixed_unit<yottatesla, yotta, tesla> {}; struct yottatesla : prefixed_unit<yottatesla, yotta, tesla> {};
struct gauss : named_scaled_unit<gauss, "G", prefix, ratio<1, 10'000>, tesla> {}; struct gauss : named_scaled_unit<gauss, "G", prefix, ratio(1, 10'000), tesla> {};
struct dim_magnetic_induction : physical::dim_magnetic_induction<dim_magnetic_induction, tesla, dim_voltage, dim_time, dim_length> {}; struct dim_magnetic_induction : physical::dim_magnetic_induction<dim_magnetic_induction, tesla, dim_voltage, dim_time, dim_length> {};

View File

@ -72,7 +72,7 @@ struct exatonne : prefixed_alias_unit<yottagram, exa, tonne> {};
struct zettatonne : prefixed_unit<zettatonne, zetta, tonne> {}; struct zettatonne : prefixed_unit<zettatonne, zetta, tonne> {};
struct yottatonne : prefixed_unit<yottatonne, yotta, tonne> {}; struct yottatonne : prefixed_unit<yottatonne, yotta, tonne> {};
struct dalton : named_scaled_unit<dalton, "Da", no_prefix, ratio<16'605'390'666'050, 10'000'000'000'000, -27>, kilogram> {}; struct dalton : named_scaled_unit<dalton, "Da", no_prefix, ratio(16'605'390'666'050, 10'000'000'000'000, -27), kilogram> {};
struct dim_mass : physical::dim_mass<kilogram> {}; struct dim_mass : physical::dim_mass<kilogram> {};

View File

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

View File

@ -37,9 +37,9 @@ struct picosecond : prefixed_unit<picosecond, pico, second> {};
struct nanosecond : prefixed_unit<nanosecond, nano, second> {}; struct nanosecond : prefixed_unit<nanosecond, nano, second> {};
struct microsecond : prefixed_unit<microsecond, micro, second> {}; struct microsecond : prefixed_unit<microsecond, micro, second> {};
struct millisecond : prefixed_unit<millisecond, milli, second> {}; struct millisecond : prefixed_unit<millisecond, milli, second> {};
struct minute : named_scaled_unit<minute, "min", no_prefix, ratio<60>, second> {}; struct minute : named_scaled_unit<minute, "min", no_prefix, ratio(60), second> {};
struct hour : named_scaled_unit<hour, "h", no_prefix, ratio<60>, minute> {}; struct hour : named_scaled_unit<hour, "h", no_prefix, ratio(60), minute> {};
struct day : named_scaled_unit<hour, "d", no_prefix, ratio<24>, hour> {}; struct day : named_scaled_unit<hour, "d", no_prefix, ratio(24), hour> {};
struct dim_time : physical::dim_time<second> {}; struct dim_time : physical::dim_time<second> {};

View File

@ -28,10 +28,10 @@
namespace units::physical::typographic { namespace units::physical::typographic {
// TODO Conflicts with (https://en.wikipedia.org/wiki/Pica_(typography)), verify correctness of below conversion factors and provide hyperlinks to definitions // TODO Conflicts with (https://en.wikipedia.org/wiki/Pica_(typography)), verify correctness of below conversion factors and provide hyperlinks to definitions
struct pica_comp : named_scaled_unit<pica_comp, "pica(comp)", no_prefix, ratio<4233333, 1000000, -3>, si::metre> {}; struct pica_comp : named_scaled_unit<pica_comp, "pica(comp)", no_prefix, ratio(4233333, 1000000, -3), si::metre> {};
struct pica_prn : named_scaled_unit<pica_prn, "pica(prn)", no_prefix, ratio<2108759, 500000, -3>, si::metre> {}; struct pica_prn : named_scaled_unit<pica_prn, "pica(prn)", no_prefix, ratio(2108759, 500000, -3), si::metre> {};
struct point_comp : named_scaled_unit<point_comp, "point(comp)", no_prefix, ratio<1763889, 500000, -4>, si::metre> {}; struct point_comp : named_scaled_unit<point_comp, "point(comp)", no_prefix, ratio(1763889, 500000, -4), si::metre> {};
struct point_prn : named_scaled_unit<point_prn, "point(prn)", no_prefix, ratio<1757299, 500000, -4>, si::metre> {}; struct point_prn : named_scaled_unit<point_prn, "point(prn)", no_prefix, ratio(1757299, 500000, -4), si::metre> {};
inline namespace literals { inline namespace literals {

View File

@ -28,14 +28,14 @@ namespace units::physical::us {
// https://en.wikipedia.org/wiki/Foot_(unit)#US_survey_foot // https://en.wikipedia.org/wiki/Foot_(unit)#US_survey_foot
// https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6
struct foot : named_scaled_unit<foot, "ft(us)", no_prefix, ratio<1'200, 3'937>, si::metre> {}; struct foot : named_scaled_unit<foot, "ft(us)", no_prefix, ratio(1'200, 3'937), si::metre> {};
// https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6
struct fathom : named_scaled_unit<fathom, "fathom(us)", no_prefix, ratio<6>, foot> {}; struct fathom : named_scaled_unit<fathom, "fathom(us)", no_prefix, ratio(6), foot> {};
// https://en.wikipedia.org/wiki/Mile#U.S._survey_mile // https://en.wikipedia.org/wiki/Mile#U.S._survey_mile
// https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6
struct mile : named_scaled_unit<mile, "mi(us)", no_prefix, ratio<5280>, us::foot> {}; struct mile : named_scaled_unit<mile, "mi(us)", no_prefix, ratio(5280), us::foot> {};
inline namespace literals { inline namespace literals {

View File

@ -45,10 +45,10 @@ struct no_prefix : prefix_family {};
namespace detail { namespace detail {
template<PrefixFamily PF, Ratio R> template<PrefixFamily PF, ratio R>
struct prefix_base : downcast_base<prefix_base<PF, R>> { struct prefix_base : downcast_base<prefix_base<PF, R>> {
using prefix_family = PF; using prefix_family = PF;
using ratio = R; static constexpr ::units::ratio ratio = R;
}; };
} // namespace detail } // namespace detail
@ -68,7 +68,7 @@ struct prefix_base : downcast_base<prefix_base<PF, R>> {
* @tparam Symbol a text representation of the prefix * @tparam Symbol a text representation of the prefix
* @tparam R factor to be used to scale a unit * @tparam R factor to be used to scale a unit
*/ */
template<typename Child, PrefixFamily PF, basic_symbol_text Symbol, Ratio R> template<typename Child, PrefixFamily PF, basic_symbol_text Symbol, ratio R>
requires (!std::same_as<PF, no_prefix>) requires (!std::same_as<PF, no_prefix>)
struct prefix : downcast_child<Child, detail::prefix_base<PF, R>> { struct prefix : downcast_child<Child, detail::prefix_base<PF, R>> {
static constexpr auto symbol = Symbol; static constexpr auto symbol = Symbol;

View File

@ -46,7 +46,7 @@ concept safe_convertible = // exposition only
template<typename Rep, typename UnitFrom, typename UnitTo> template<typename Rep, typename UnitFrom, typename UnitTo>
concept safe_divisible = // exposition only concept safe_divisible = // exposition only
treat_as_floating_point<Rep> || treat_as_floating_point<Rep> ||
ratio_divide<typename UnitFrom::ratio, typename UnitTo::ratio>::is_integral(); is_integral(UnitFrom::ratio / UnitTo::ratio);
} // namespace detail } // namespace detail
@ -344,11 +344,11 @@ template<typename D1, typename U1, typename Rep1, typename D2, typename U2, type
equivalent_dim<D1, dim_invert<D2>> equivalent_dim<D1, dim_invert<D2>>
{ {
using common_rep = decltype(lhs.count() * rhs.count()); using common_rep = decltype(lhs.count() * rhs.count());
using ratio = ratio_multiply<typename U1::ratio, typename U2::ratio>; const ratio r = U1::ratio * U2::ratio;
if constexpr (treat_as_floating_point<common_rep>) { if constexpr (treat_as_floating_point<common_rep>) {
return lhs.count() * rhs.count() * static_cast<common_rep>(ratio::num * fpow10(ratio::exp)) / static_cast<common_rep>(ratio::den); return lhs.count() * rhs.count() * static_cast<common_rep>(r.num * fpow10<common_rep>(r.exp)) / static_cast<common_rep>(r.den);
} else { } else {
return lhs.count() * rhs.count() * static_cast<common_rep>(ratio::num * ipow10(ratio::exp)) / static_cast<common_rep>(ratio::den); return lhs.count() * rhs.count() * static_cast<common_rep>(r.num * ipow10(r.exp)) / static_cast<common_rep>(r.den);
} }
} }
@ -357,10 +357,7 @@ template<typename D1, typename U1, typename Rep1, typename D2, typename U2, type
requires std::regular_invocable<std::multiplies<>, Rep1, Rep2> requires std::regular_invocable<std::multiplies<>, Rep1, Rep2>
{ {
using dim = dimension_multiply<D1, D2>; using dim = dimension_multiply<D1, D2>;
using ratio1 = ratio_divide<typename U1::ratio, typename dimension_unit<D1>::ratio>; using unit = downcast_unit<dim, (U1::ratio / dimension_unit<D1>::ratio) * (U2::ratio / dimension_unit<D2>::ratio) * dimension_unit<dim>::ratio>;
using ratio2 = ratio_divide<typename U2::ratio, typename dimension_unit<D2>::ratio>;
using ratio = ratio_multiply<ratio_multiply<ratio1, ratio2>, typename dimension_unit<dim>::ratio>;
using unit = downcast_unit<dim, ratio>;
using common_rep = decltype(lhs.count() * rhs.count()); using common_rep = decltype(lhs.count() * rhs.count());
using ret = quantity<dim, unit, common_rep>; using ret = quantity<dim, unit, common_rep>;
return ret(lhs.count() * rhs.count()); return ret(lhs.count() * rhs.count());
@ -373,8 +370,7 @@ template<Scalar Value, typename D, typename U, typename Rep>
Expects(q.count() != 0); Expects(q.count() != 0);
using dim = dim_invert<D>; using dim = dim_invert<D>;
using ratio = ratio<U::ratio::den, U::ratio::num, -U::ratio::exp>; using unit = downcast_unit<dim, ratio(U::ratio.den, U::ratio.num, -U::ratio.exp)>;
using unit = downcast_unit<dim, ratio>;
using common_rep = decltype(v / q.count()); using common_rep = decltype(v / q.count());
using ret = quantity<dim, unit, common_rep>; using ret = quantity<dim, unit, common_rep>;
return ret(v / q.count()); return ret(v / q.count());
@ -411,10 +407,7 @@ template<typename D1, typename U1, typename Rep1, typename D2, typename U2, type
using common_rep = decltype(lhs.count() / rhs.count()); using common_rep = decltype(lhs.count() / rhs.count());
using dim = dimension_divide<D1, D2>; using dim = dimension_divide<D1, D2>;
using ratio1 = ratio_divide<typename U1::ratio, typename dimension_unit<D1>::ratio>; using unit = downcast_unit<dim, (U1::ratio / dimension_unit<D1>::ratio) / (U2::ratio / dimension_unit<D2>::ratio) * dimension_unit<dim>::ratio>;
using ratio2 = ratio_divide<typename U2::ratio, typename dimension_unit<D2>::ratio>;
using ratio = ratio_multiply<ratio_divide<ratio1, ratio2>, typename dimension_unit<dim>::ratio>;
using unit = downcast_unit<dim, ratio>;
using ret = quantity<dim, unit, common_rep>; using ret = quantity<dim, unit, common_rep>;
return ret(lhs.count() / rhs.count()); return ret(lhs.count() / rhs.count());
} }

View File

@ -69,10 +69,10 @@ concept QuantityOf = Quantity<T> && Dimension<Dim> && equivalent_dim<typename T:
// quantity_cast // quantity_cast
namespace detail { namespace detail {
template<typename To, typename CRatio, typename CRep, bool NumIsOne, bool DenIsOne, bool ExpIsZero> template<typename To, ratio CRatio, typename CRep, bool NumIsOne, bool DenIsOne, bool ExpIsZero>
struct quantity_cast_impl; struct quantity_cast_impl;
template<typename To, typename CRatio, typename CRep> template<typename To, ratio CRatio, typename CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, true, true> { struct quantity_cast_impl<To, CRatio, CRep, true, true, true> {
template<Quantity Q> template<Quantity Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
@ -81,235 +81,227 @@ struct quantity_cast_impl<To, CRatio, CRep, true, true, true> {
} }
}; };
template<typename To, typename CRatio, constructible_from_integral CRep> template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, true, false> { struct quantity_cast_impl<To, CRatio, CRep, true, true, false> {
template<Quantity Q> template<Quantity Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
if constexpr (treat_as_floating_point<CRep>) { if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(fpow10<CRep>(CRatio::exp)))); return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(fpow10<CRep>(CRatio.exp))));
} else { } else {
if constexpr (CRatio::exp > 0) { if constexpr (CRatio.exp > 0) {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(ipow10(CRatio::exp)))); return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(ipow10(CRatio.exp))));
} }
else { else {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) / static_cast<CRep>(ipow10(-CRatio::exp)))); return To(static_cast<To::rep>(static_cast<CRep>(q.count()) / static_cast<CRep>(ipow10(-CRatio.exp))));
} }
} }
} }
}; };
template<typename To, typename CRatio, constructible_from_integral CRep> template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, false, true> { struct quantity_cast_impl<To, CRatio, CRep, false, false, true> {
template<typename Q> template<typename Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * return To(static_cast<To::rep>(static_cast<CRep>(q.count()) *
(static_cast<CRep>(CRatio::num) / (static_cast<CRep>(CRatio.num) /
static_cast<CRep>(CRatio::den)))); static_cast<CRep>(CRatio.den))));
} }
}; };
template<typename To, typename CRatio, constructible_from_integral CRep> template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, false, false> { struct quantity_cast_impl<To, CRatio, CRep, false, false, false> {
template<typename Q> template<typename Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
if constexpr (treat_as_floating_point<CRep>) { if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * return To(static_cast<To::rep>(static_cast<CRep>(q.count()) *
static_cast<CRep>(fpow10<CRep>(CRatio::exp)) * static_cast<CRep>(fpow10<CRep>(CRatio.exp)) *
(static_cast<CRep>(CRatio::num) / (static_cast<CRep>(CRatio.num) /
static_cast<CRep>(CRatio::den)))); static_cast<CRep>(CRatio.den))));
} else { } else {
if constexpr (CRatio::exp > 0) { if constexpr (CRatio.exp > 0) {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * return To(static_cast<To::rep>(static_cast<CRep>(q.count()) *
static_cast<CRep>(CRatio::num) * static_cast<CRep>(CRatio.num) *
static_cast<CRep>(ipow10(CRatio::exp)) / static_cast<CRep>(ipow10(CRatio.exp)) /
static_cast<CRep>(CRatio::den))); static_cast<CRep>(CRatio.den)));
} }
else { else {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * return To(static_cast<To::rep>(static_cast<CRep>(q.count()) *
static_cast<CRep>(CRatio::num) / static_cast<CRep>(CRatio.num) /
(static_cast<CRep>(CRatio::den) * (static_cast<CRep>(CRatio.den) *
static_cast<CRep>(ipow10(-CRatio::exp))))); static_cast<CRep>(ipow10(-CRatio.exp)))));
} }
} }
} }
}; };
template<typename To, typename CRatio, constructible_from_integral CRep> template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, false, true> { struct quantity_cast_impl<To, CRatio, CRep, true, false, true> {
template<Quantity Q> template<Quantity Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) / static_cast<CRep>(CRatio::den))); return To(static_cast<To::rep>(static_cast<CRep>(q.count()) / static_cast<CRep>(CRatio.den)));
} }
}; };
template<typename To, typename CRatio, constructible_from_integral CRep> template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, false, false> { struct quantity_cast_impl<To, CRatio, CRep, true, false, false> {
template<Quantity Q> template<Quantity Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
if constexpr (treat_as_floating_point<CRep>) { if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(fpow10<CRep>(CRatio::exp)) * (CRep{1} / static_cast<CRep>(CRatio::den)))); return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(fpow10<CRep>(CRatio.exp)) * (CRep{1} / static_cast<CRep>(CRatio.den))));
} else { } else {
if constexpr (CRatio::exp > 0) { if constexpr (CRatio.exp > 0) {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(ipow10(CRatio::exp)) / static_cast<CRep>(CRatio::den))); return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(ipow10(CRatio.exp)) / static_cast<CRep>(CRatio.den)));
} }
else { else {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) / (static_cast<CRep>(ipow10(-CRatio::exp)) * static_cast<CRep>(CRatio::den)))); return To(static_cast<To::rep>(static_cast<CRep>(q.count()) / (static_cast<CRep>(ipow10(-CRatio.exp)) * static_cast<CRep>(CRatio.den))));
} }
} }
} }
}; };
template<typename To, typename CRatio, constructible_from_integral CRep> template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, true, true> { struct quantity_cast_impl<To, CRatio, CRep, false, true, true> {
template<Quantity Q> template<Quantity Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio::num))); return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio.num)));
} }
}; };
template<typename To, typename CRatio, constructible_from_integral CRep> template<typename To, ratio CRatio, constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, true, false> { struct quantity_cast_impl<To, CRatio, CRep, false, true, false> {
template<Quantity Q> template<Quantity Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
if constexpr (treat_as_floating_point<CRep>) { if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio::num) * static_cast<CRep>(fpow10<CRep>(CRatio::exp)))); return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio.num) * static_cast<CRep>(fpow10<CRep>(CRatio.exp))));
} else { } else {
if constexpr (CRatio::exp > 0) { if constexpr (CRatio.exp > 0) {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio::num) * static_cast<CRep>(ipow10(CRatio::exp)))); return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio.num) * static_cast<CRep>(ipow10(CRatio.exp))));
} }
else { else {
return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio::num) / static_cast<CRep>(ipow10(-CRatio::exp)))); return To(static_cast<To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio.num) / static_cast<CRep>(ipow10(-CRatio.exp))));
} }
} }
} }
}; };
template<typename To, typename CRatio, not_constructible_from_integral CRep> template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, true, false> { struct quantity_cast_impl<To, CRatio, CRep, true, true, false> {
template<Quantity Q> template<Quantity Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
if constexpr (treat_as_floating_point<CRep>) { if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<To::rep>(q.count() * fpow10<CRep>(CRatio::exp))); return To(static_cast<To::rep>(q.count() * fpow10<CRep>(CRatio.exp)));
} else { } else {
if constexpr (CRatio::exp > 0) { if constexpr (CRatio.exp > 0) {
return To(static_cast<To::rep>(q.count() * ipow10(CRatio::exp))); return To(static_cast<To::rep>(q.count() * ipow10(CRatio.exp)));
} }
else { else {
return To(static_cast<To::rep>(q.count() / ipow10(-CRatio::exp))); return To(static_cast<To::rep>(q.count() / ipow10(-CRatio.exp)));
} }
} }
} }
}; };
template<typename To, typename CRatio, not_constructible_from_integral CRep> template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, false, true> { struct quantity_cast_impl<To, CRatio, CRep, false, false, true> {
template<typename Q> template<typename Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
return To(static_cast<To::rep>(q.count() * (CRatio::num / CRatio::den))); return To(static_cast<To::rep>(q.count() * (CRatio.num / CRatio.den)));
} }
}; };
template<typename To, typename CRatio, not_constructible_from_integral CRep> template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, false, false> { struct quantity_cast_impl<To, CRatio, CRep, false, false, false> {
template<typename Q> template<typename Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
if constexpr (treat_as_floating_point<CRep>) { if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<To::rep>(q.count() * fpow10<CRep>(CRatio::exp) * (CRatio::num / CRatio::den))); return To(static_cast<To::rep>(q.count() * fpow10<CRep>(CRatio.exp) * (CRatio.num / CRatio.den)));
} else { } else {
if constexpr (CRatio::exp > 0) { if constexpr (CRatio.exp > 0) {
return To(static_cast<To::rep>(q.count() * CRatio::num * ipow10(CRatio::exp) / CRatio::den)); return To(static_cast<To::rep>(q.count() * CRatio.num * ipow10(CRatio.exp) / CRatio.den));
} }
else { else {
return To(static_cast<To::rep>(q.count()) * CRatio::num / (CRatio::den * ipow10(-CRatio::exp))); return To(static_cast<To::rep>(q.count()) * CRatio.num / (CRatio.den * ipow10(-CRatio.exp)));
} }
} }
} }
}; };
template<typename To, typename CRatio, not_constructible_from_integral CRep> template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, false, true> { struct quantity_cast_impl<To, CRatio, CRep, true, false, true> {
template<Quantity Q> template<Quantity Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
return To(static_cast<To::rep>(q.count() / CRatio::den)); return To(static_cast<To::rep>(q.count() / CRatio.den));
} }
}; };
template<typename To, typename CRatio, not_constructible_from_integral CRep> template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, true, false, false> { struct quantity_cast_impl<To, CRatio, CRep, true, false, false> {
template<Quantity Q> template<Quantity Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
if constexpr (treat_as_floating_point<CRep>) { if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<To::rep>(q.count() * fpow10<CRep>(CRatio::exp) / CRatio::den)); return To(static_cast<To::rep>(q.count() * fpow10<CRep>(CRatio.exp) / CRatio.den));
} else { } else {
if constexpr (CRatio::exp > 0) { if constexpr (CRatio.exp > 0) {
return To(static_cast<To::rep>(q.count() * ipow10(CRatio::exp) / CRatio::den)); return To(static_cast<To::rep>(q.count() * ipow10(CRatio.exp) / CRatio.den));
} }
else { else {
return To(static_cast<To::rep>(q.count() / (ipow10(-CRatio::exp) * CRatio::den))); return To(static_cast<To::rep>(q.count() / (ipow10(-CRatio.exp) * CRatio.den)));
} }
} }
} }
}; };
template<typename To, typename CRatio, not_constructible_from_integral CRep> template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, true, true> { struct quantity_cast_impl<To, CRatio, CRep, false, true, true> {
template<Quantity Q> template<Quantity Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
return To(static_cast<To::rep>(q.count() * CRatio::num)); return To(static_cast<To::rep>(q.count() * CRatio.num));
} }
}; };
template<typename To, typename CRatio, not_constructible_from_integral CRep> template<typename To, ratio CRatio, not_constructible_from_integral CRep>
struct quantity_cast_impl<To, CRatio, CRep, false, true, false> { struct quantity_cast_impl<To, CRatio, CRep, false, true, false> {
template<Quantity Q> template<Quantity Q>
static constexpr To cast(const Q& q) static constexpr To cast(const Q& q)
{ {
if constexpr (treat_as_floating_point<CRep>) { if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<To::rep>(q.count() * CRatio::num * fpow10<CRep>(CRatio::exp))); return To(static_cast<To::rep>(q.count() * CRatio.num * fpow10<CRep>(CRatio.exp)));
} else { } else {
if constexpr (CRatio::exp > 0) { if constexpr (CRatio.exp > 0) {
return To(static_cast<To::rep>(q.count() * CRatio::num * ipow10(CRatio::exp))); return To(static_cast<To::rep>(q.count() * CRatio.num * ipow10(CRatio.exp)));
} }
else { else {
return To(static_cast<To::rep>(q.count() * CRatio::num / ipow10(-CRatio::exp))); return To(static_cast<To::rep>(q.count() * CRatio.num / ipow10(-CRatio.exp)));
} }
} }
} }
}; };
template<Dimension FromD, Unit FromU, Dimension ToD, Unit ToU> template<Dimension FromD, Unit FromU, Dimension ToD, Unit ToU>
struct cast_ratio; constexpr ratio cast_ratio()
{
template<BaseDimension FromD, Unit FromU, BaseDimension ToD, Unit ToU> if constexpr(BaseDimension<FromD> || same_unit_reference<FromU, ToU>::value) {
struct cast_ratio<FromD, FromU, ToD, ToU> { return FromU::ratio / ToU::ratio;
using type = ratio_divide<typename FromU::ratio, typename ToU::ratio>; }
}; else {
const ratio from_ratio = FromD::base_units_ratio * FromU::ratio;
template<DerivedDimension FromD, Unit FromU, DerivedDimension ToD, Unit ToU> const ratio to_ratio = ToD::base_units_ratio * ToU::ratio;
requires same_unit_reference<FromU, ToU>::value return from_ratio / to_ratio;
struct cast_ratio<FromD, FromU, ToD, ToU> { }
using type = ratio_divide<typename FromU::ratio, typename ToU::ratio>; }
};
template<DerivedDimension FromD, Unit FromU, DerivedDimension ToD, Unit ToU>
struct cast_ratio<FromD, FromU, ToD, ToU> {
using from_ratio = ratio_multiply<typename FromD::base_units_ratio, typename FromU::ratio>;
using to_ratio = ratio_multiply<typename ToD::base_units_ratio, typename ToU::ratio>;
using type = ratio_divide<from_ratio, to_ratio>;
};
} // namespace detail } // namespace detail
@ -329,11 +321,11 @@ template<Quantity To, typename D, typename U, typename Rep>
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q) [[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
requires QuantityOf<To, D> requires QuantityOf<To, D>
{ {
using c_ratio = detail::cast_ratio<D, U, typename To::dimension, typename To::unit>::type; using c_ratio = std::integral_constant<ratio, detail::cast_ratio<D, U, typename To::dimension, typename To::unit>()>;
using c_rep = std::common_type_t<typename To::rep, Rep>; using c_rep = std::common_type_t<typename To::rep, Rep>;
using ret_unit = downcast_unit<typename To::dimension, typename To::unit::ratio>; using ret_unit = downcast_unit<typename To::dimension, To::unit::ratio>;
using ret = quantity<typename To::dimension, ret_unit, typename To::rep>; using ret = quantity<typename To::dimension, ret_unit, typename To::rep>;
using cast = detail::quantity_cast_impl<ret, c_ratio, c_rep, c_ratio::num == 1, c_ratio::den == 1, c_ratio::exp == 0>; using cast = detail::quantity_cast_impl<ret, c_ratio::value, c_rep, c_ratio::value.num == 1, c_ratio::value.den == 1, c_ratio::value.exp == 0>;
return cast::cast(q); return cast::cast(q);
} }

View File

@ -23,7 +23,6 @@
#pragma once #pragma once
#include <units/bits/external/hacks.h> #include <units/bits/external/hacks.h>
#include <units/concepts.h>
#include <units/bits/ratio_maths.h> #include <units/bits/ratio_maths.h>
#include <cstdint> #include <cstdint>
#include <numeric> #include <numeric>
@ -33,192 +32,91 @@
namespace units { namespace units {
struct ratio;
constexpr ratio inverse(const ratio& r);
/** /**
* @brief Provides compile-time rational arithmetic support. * @brief Provides compile-time rational arithmetic support.
* *
* This class is really similar to @c std::ratio but gets an additional `Exp` * This class is really similar to @c std::ratio but gets an additional `Exp`
* template parameter. * template parameter that defines the exponent of the ratio. Another important
* * difference is the fact that the objects of that class are used as class NTTPs
* @tparam Num Numerator * rather then a type template parameter kind.
* @tparam Den Denominator (default @c 1)
* @tparam Exp Exponent (default @c 0)
*/ */
template<std::intmax_t Num, std::intmax_t Den = 1, std::intmax_t Exp = 0>
requires(Den != 0)
struct ratio { struct ratio {
static_assert(-INTMAX_MAX <= Num, "numerator too negative"); std::intmax_t num;
static_assert(-INTMAX_MAX <= Den, "denominator too negative"); std::intmax_t den;
std::intmax_t exp;
private: explicit constexpr ratio(std::intmax_t n, std::intmax_t d = 1, std::intmax_t e = 0): num(n), den(d), exp(e)
static constexpr auto norm = detail::normalize(Num, Den, Exp); {
detail::normalize(num, den, exp);
}
public: #if __GNUC__ >= 10
static constexpr std::intmax_t num = norm[0];
static constexpr std::intmax_t den = norm[1];
static constexpr std::intmax_t exp = norm[2];
using type = ratio<num, den, exp>; [[nodiscard]] friend constexpr bool operator==(const ratio&, const ratio&) = default;
static constexpr bool is_integral() { #else
if constexpr (exp < 0) {
return false; [[nodiscard]] friend constexpr bool operator==(const ratio& lhs, const ratio& rhs)
} else { {
return detail::gcdpow(num, exp, den) == den; return lhs.num == rhs.num && lhs.den == rhs.den && lhs.exp == rhs.exp;
} }
[[nodiscard]] friend constexpr bool operator!=(const ratio& lhs, const ratio& rhs)
{
return !(lhs == rhs);
}
#endif
[[nodiscard]] friend constexpr ratio operator*(const ratio& lhs, const ratio& rhs)
{
const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den);
const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den);
return ratio(detail::safe_multiply(lhs.num / gcd1, rhs.num / gcd2),
detail::safe_multiply(lhs.den / gcd2, rhs.den / gcd1),
lhs.exp + rhs.exp);
}
[[nodiscard]] friend constexpr ratio operator/(const ratio& lhs, const ratio& rhs)
{
return lhs * inverse(rhs);
} }
}; };
namespace detail { [[nodiscard]] constexpr ratio inverse(const ratio& r)
template<intmax_t Num, intmax_t Den, intmax_t Exp>
inline constexpr bool is_ratio<ratio<Num, Den, Exp>> = true;
// unused, and align exponents process could be subject to overflow in extreme cases
// template<Ratio R1, Ratio R2>
// constexpr auto ratio_add_detail() {
// std::intmax_t num1 = R1::num;
// std::intmax_t num2 = R2::num;
// // align exponents
// std::intmax_t new_exp = R1::exp;
// if constexpr (R1::exp > R2::exp) {
// new_exp = R1::exp;
// while (new_exp > R2::exp) {
// num1 *= 10;
// --new_exp;
// }
// } else if constexpr (R1::exp < R2::exp) {
// new_exp = R2::exp;
// while (R1::exp < new_exp) {
// num2 *= 10;
// --new_exp;
// }
// }
// // common denominator
// std::intmax_t lcm_den = std::lcm(R1::den, R2::den);
// num1 = num1 * (lcm_den / R1::den);
// num2 = num2 * (lcm_den / R2::den);
// return std::array{num1 + num2, lcm_den, new_exp};
// }
// template<Ratio R1, Ratio R2>
// struct ratio_add_impl {
// static constexpr auto detail = ratio_add_detail<R1, R2>();
// using type = ratio<detail[0], detail[1], detail[2]>;
// };
}// namespace detail
// ratio_add : not used
// template<Ratio R1, Ratio R2>
// using ratio_add = detail::ratio_add_impl<R1, R2>::type;
// ratio_subtract : not used
// TODO implement ratio_subtract
// template<Ratio R1, Ratio R2>
// using ratio_subtract = detail::ratio_subtract_impl<R1, R2>::type;
// ratio_multiply
namespace detail {
static constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs)
{ {
constexpr std::intmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4); return ratio(r.den, r.num, -r.exp);
const std::intmax_t a0 = detail::abs(lhs) % c;
const std::intmax_t a1 = detail::abs(lhs) / c;
const std::intmax_t b0 = detail::abs(rhs) % c;
const std::intmax_t b1 = detail::abs(rhs) / c;
Expects(a1 == 0 || b1 == 0); // overflow in multiplication
Expects(a0 * b1 + b0 * a1 < (c >> 1)); // overflow in multiplication
Expects(b0 * a0 <= INTMAX_MAX); // overflow in multiplication
Expects((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); // overflow in multiplication
return lhs * rhs;
} }
template<typename R1, typename R2> [[nodiscard]] constexpr bool is_integral(const ratio& r)
struct ratio_multiply_impl { {
private: if(r.exp < 0) {
static constexpr std::intmax_t gcd1 = std::gcd(R1::num, R2::den); return false;
static constexpr std::intmax_t gcd2 = std::gcd(R2::num, R1::den); } else {
return detail::gcdpow(r.num, r.exp, r.den) == r.den;
}
}
static constexpr auto norm = detail::normalize(safe_multiply(R1::num / gcd1, R2::num / gcd2), template<std::intmax_t N>
safe_multiply(R1::den / gcd2, R2::den / gcd1), [[nodiscard]] constexpr ratio pow(const ratio& r)
R1::exp + R2::exp); {
if constexpr(N == 0)
static constexpr std::intmax_t norm_num = norm[0]; return ratio(1);
static constexpr std::intmax_t norm_den = norm[1]; else if constexpr(N == 1)
static constexpr std::intmax_t norm_exp = norm[2]; return r;
else
public: return pow<N-1>(r) * r;
using type = ratio<norm_num, norm_den, norm_exp>; }
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
template<Ratio R1, Ratio R2>
using ratio_multiply = detail::ratio_multiply_impl<R1, R2>::type;
// ratio_divide
namespace detail {
template<typename R1, typename R2>
struct ratio_divide_impl {
static_assert(R2::num != 0, "division by 0");
using type = ratio_multiply<R1, ratio<R2::den, R2::num, -R2::exp>>;
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
template<Ratio R1, Ratio R2>
using ratio_divide = detail::ratio_divide_impl<R1, R2>::type;
// ratio_pow
namespace detail {
template<typename R, std::intmax_t N>
struct ratio_pow_impl {
using type = ratio_multiply<typename ratio_pow_impl<R, N - 1>::type, R>;
};
template<typename R>
struct ratio_pow_impl<R, 1> {
using type = R;
};
template<typename R>
struct ratio_pow_impl<R, 0> {
using type = ratio<1>;
};
} // namespace detail
template<Ratio R, std::intmax_t N>
using ratio_pow = detail::ratio_pow_impl<R, N>::type;
// ratio_sqrt
namespace detail { namespace detail {
// sqrt_impl avoids overflow and recursion // sqrt_impl avoids overflow and recursion
// from http://www.codecodex.com/wiki/Calculate_an_integer_square_root#C.2B.2B // from http://www.codecodex.com/wiki/Calculate_an_integer_square_root#C.2B.2B
// if v >= place this will fail (so we can't quite use the last bit) // if v >= place this will fail (so we can't quite use the last bit)
static constexpr std::intmax_t sqrt_impl(std::intmax_t v) [[nodiscard]] constexpr std::intmax_t sqrt_impl(std::intmax_t v)
{ {
// place = 0x4000 0000 for 32bit // place = 0x4000 0000 for 32bit
// place = 0x4000 0000 0000 0000 for 64bit // place = 0x4000 0000 0000 0000 for 64bit
@ -237,52 +135,34 @@ static constexpr std::intmax_t sqrt_impl(std::intmax_t v)
return root; return root;
} }
template<Ratio R> [[nodiscard]] constexpr auto make_exp_even(const ratio& r)
constexpr auto make_exp_even()
{ {
if constexpr (R::exp % 2 == 0) if(r.exp % 2 == 0)
return std::array{R::num, R::den, R::exp}; // already even (incl zero) return std::array{r.num, r.den, r.exp}; // already even (incl zero)
// safely make exp even, so it can be divided by 2 for square root // safely make exp even, so it can be divided by 2 for square root
if constexpr (R::exp > 0) if(r.exp > 0)
return std::array{R::num * 10, R::den, R::exp - 1}; return std::array{r.num * 10, r.den, r.exp - 1};
else else
return std::array{R::num, R::den * 10, R::exp + 1}; return std::array{r.num, r.den * 10, r.exp + 1};
} }
template<typename R>
struct ratio_sqrt_impl {
constexpr static auto even = detail::make_exp_even<R>();
using type = ratio<detail::sqrt_impl(even[0]), detail::sqrt_impl(even[1]), even[2] / 2>;
};
template<std::intmax_t Den>
struct ratio_sqrt_impl<ratio<0, Den>> {
using type = ratio<0>;
};
} // namespace detail } // namespace detail
template<Ratio R> [[nodiscard]] constexpr ratio sqrt(const ratio& r)
using ratio_sqrt = detail::ratio_sqrt_impl<R>::type; {
if(r.num == 0)
return ratio(0);
const auto even = detail::make_exp_even(r);
return ratio(detail::sqrt_impl(even[0]), detail::sqrt_impl(even[1]), even[2] / 2);
}
// common_ratio // common_ratio
[[nodiscard]] constexpr ratio common_ratio(const ratio& r1, const ratio& r2)
namespace detail { {
const auto res = detail::gcd_frac(r1.num, r1.den, r1.exp, r2.num, r2.den, r2.exp);
// TODO: simplified return ratio(res[0], res[1], res[2]);
template<typename R1, typename R2> }
struct common_ratio_impl {
static constexpr auto res = gcd_frac(R1::num, R1::den, R1::exp, R2::num, R2::den, R2::exp);
static constexpr std::intmax_t num = res[0];
static constexpr std::intmax_t den = res[1];
static constexpr std::intmax_t exp = res[2];
using type = ratio<num, den, exp>;
};
} // namespace detail
template<Ratio R1, Ratio R2>
using common_ratio = detail::common_ratio_impl<R1, R2>::type;
} // namespace units } // namespace units

View File

@ -47,16 +47,19 @@ namespace units {
* (i.e. all length units are expressed in terms of meter, all mass units are expressed in * (i.e. all length units are expressed in terms of meter, all mass units are expressed in
* terms of gram, ...) * terms of gram, ...)
* *
* @tparam U a unit to use as a reference for this dimension
* @tparam R a ratio of a reference unit * @tparam R a ratio of a reference unit
* @tparam U a unit to use as a reference for this dimension
*/ */
template<UnitRatio R, typename U> template<ratio R, typename U>
requires UnitRatio<R>
struct scaled_unit : downcast_base<scaled_unit<R, U>> { struct scaled_unit : downcast_base<scaled_unit<R, U>> {
using ratio = R; static constexpr ::units::ratio ratio = R;
using reference = U; using reference = U;
}; };
template<Dimension D, UnitRatio R> template<Dimension D, auto R>
// template<Dimension D, ratio R> // TODO: GCC crash!!!
requires UnitRatio<R>
using downcast_unit = downcast<scaled_unit<R, typename dimension_unit<D>::reference>>; using downcast_unit = downcast<scaled_unit<R, typename dimension_unit<D>::reference>>;
template<Unit U1, Unit U2> template<Unit U1, Unit U2>
@ -71,7 +74,7 @@ struct same_unit_reference : std::is_same<typename U1::reference, typename U2::r
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
*/ */
template<typename Child> template<typename Child>
struct unit : downcast_child<Child, scaled_unit<ratio<1>, Child>> { struct unit : downcast_child<Child, scaled_unit<ratio(1), Child>> {
static constexpr bool is_named = false; static constexpr bool is_named = false;
using prefix_family = no_prefix; using prefix_family = no_prefix;
}; };
@ -96,7 +99,7 @@ struct unknown_coherent_unit : unit<unknown_coherent_unit> {};
* @tparam PF no_prefix or a type of prefix family * @tparam PF no_prefix or a type of prefix family
*/ */
template<typename Child, basic_symbol_text Symbol, PrefixFamily PF> template<typename Child, basic_symbol_text Symbol, PrefixFamily PF>
struct named_unit : downcast_child<Child, scaled_unit<ratio<1>, Child>> { struct named_unit : downcast_child<Child, scaled_unit<ratio(1), Child>> {
static constexpr bool is_named = true; static constexpr bool is_named = true;
static constexpr auto symbol = Symbol; static constexpr auto symbol = Symbol;
using prefix_family = PF; using prefix_family = PF;
@ -116,8 +119,9 @@ struct named_unit : downcast_child<Child, scaled_unit<ratio<1>, Child>> {
* @tparam R a scale to apply to U * @tparam R a scale to apply to U
* @tparam U a reference unit to scale * @tparam U a reference unit to scale
*/ */
template<typename Child, basic_symbol_text Symbol, PrefixFamily PF, UnitRatio R, Unit U> template<typename Child, basic_symbol_text Symbol, PrefixFamily PF, ratio R, Unit U>
struct named_scaled_unit : downcast_child<Child, scaled_unit<ratio_multiply<R, typename U::ratio>, typename U::reference>> { requires UnitRatio<R>
struct named_scaled_unit : downcast_child<Child, scaled_unit<R * U::ratio, typename U::reference>> {
static constexpr bool is_named = true; static constexpr bool is_named = true;
static constexpr auto symbol = Symbol; static constexpr auto symbol = Symbol;
using prefix_family = PF; using prefix_family = PF;
@ -136,8 +140,7 @@ struct named_scaled_unit : downcast_child<Child, scaled_unit<ratio_multiply<R, t
*/ */
template<typename Child, Prefix P, Unit U> template<typename Child, Prefix P, Unit U>
requires U::is_named && std::same_as<typename P::prefix_family, typename U::prefix_family> requires U::is_named && std::same_as<typename P::prefix_family, typename U::prefix_family>
struct prefixed_unit : struct prefixed_unit : downcast_child<Child, scaled_unit<P::ratio * U::ratio, typename U::reference>> {
downcast_child<Child, scaled_unit<ratio_multiply<typename P::ratio, typename U::ratio>, typename U::reference>> {
static constexpr bool is_named = true; static constexpr bool is_named = true;
static constexpr auto symbol = P::symbol + U::symbol; static constexpr auto symbol = P::symbol + U::symbol;
using prefix_family = no_prefix; using prefix_family = no_prefix;

View File

@ -115,7 +115,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
{ {
SECTION("in terms of base units") SECTION("in terms of base units")
{ {
const length<scaled_unit<ratio<1, 1, 6>, metre>> q(123); const length<scaled_unit<ratio(1, 1, 6), metre>> q(123);
os << q; os << q;
SECTION("iostream") SECTION("iostream")
@ -136,7 +136,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
SECTION("in terms of derived units") SECTION("in terms of derived units")
{ {
const energy<scaled_unit<ratio<1, 1, -2>, joule>> q(60); const energy<scaled_unit<ratio(1, 1, -2), joule>> q(60);
os << q; os << q;
SECTION("iostream") SECTION("iostream")

View File

@ -319,7 +319,7 @@ TEST_CASE("fmt::format on synthesized unit symbols", "[text][fmt]")
SECTION("unknown scaled unit with reference different than the dimension's coherent unit") SECTION("unknown scaled unit with reference different than the dimension's coherent unit")
{ {
CHECK(fmt::format("{}", mass<units::scaled_unit<units::ratio<2, 3>, gram>>(1)) == "1 [2/3 × 10⁻³] kg"); CHECK(fmt::format("{}", mass<units::scaled_unit<units::ratio(2, 3), gram>>(1)) == "1 [2/3 × 10⁻³] kg");
CHECK(fmt::format("{:%Q %Aq}", mass<units::scaled_unit<units::ratio<2, 3>, gram>>(1)) == "1 [2/3 x 10^-3] kg"); CHECK(fmt::format("{:%Q %Aq}", mass<units::scaled_unit<units::ratio(2, 3), gram>>(1)) == "1 [2/3 x 10^-3] kg");
} }
} }

View File

@ -57,7 +57,7 @@ static_assert(10q_cm == 2q_cm_per_s * 5q_s);
static_assert(detail::unit_text<dim_speed, centimetre_per_second>() == "cm/s"); static_assert(detail::unit_text<dim_speed, centimetre_per_second>() == "cm/s");
// area // area
static_assert(std::is_same_v<ratio_divide<centimetre::ratio, dimension_unit<dim_length>::ratio>, ratio<1>>); static_assert(centimetre::ratio / dimension_unit<dim_length>::ratio == ratio(1));
static_assert(1q_cm * 1q_cm == 1q_cm2); static_assert(1q_cm * 1q_cm == 1q_cm2);
static_assert(100q_cm2 / 10q_cm == 10q_cm); static_assert(100q_cm2 / 10q_cm == 10q_cm);

View File

@ -57,7 +57,7 @@ static_assert(10q_ft == 2q_ft_per_s * 5q_s);
static_assert(detail::unit_text<dim_speed, foot_per_second>() == "ft/s"); static_assert(detail::unit_text<dim_speed, foot_per_second>() == "ft/s");
// area // area
static_assert(std::is_same_v<ratio_divide<foot::ratio, dimension_unit<dim_length>::ratio>, ratio<1>>); static_assert(foot::ratio / dimension_unit<dim_length>::ratio == ratio(1));
static_assert(1q_ft * 1q_ft == 1q_ft2); static_assert(1q_ft * 1q_ft == 1q_ft2);
static_assert(100q_ft2 / 10q_ft == 10q_ft); static_assert(100q_ft2 / 10q_ft == 10q_ft);

View File

@ -209,7 +209,7 @@ static_assert(std::equality_comparable_with<decltype(quantity_point(1q_m)), decl
// quantity_cast // quantity_cast
static_assert( static_assert(
std::is_same_v<decltype(quantity_point_cast<scaled_unit<ratio<1>, metre>>(quantity_point(2q_km)))::unit, metre>); std::is_same_v<decltype(quantity_point_cast<scaled_unit<ratio(1), metre>>(quantity_point(2q_km)))::unit, metre>);
static_assert(quantity_point_cast<quantity_point<dim_length, metre, int>>(quantity_point(2q_km)).relative().count() == static_assert(quantity_point_cast<quantity_point<dim_length, metre, int>>(quantity_point(2q_km)).relative().count() ==
2000); 2000);

View File

@ -39,7 +39,7 @@ using namespace units::physical::si;
// constexpr quantity<si::dim_length, second, int> error(0); // should not compile (unit of a different dimension) // constexpr quantity<si::dim_length, second, int> error(0); // should not compile (unit of a different dimension)
// constexpr quantity<si::dim_length, metre, quantity<si::dim_length, metre, int>> error(0); // should not compile (quantity used as Rep) // constexpr quantity<si::dim_length, metre, quantity<si::dim_length, metre, int>> error(0); // should not compile (quantity used as Rep)
// constexpr quantity<metre, si::dim_length, double> error(0); // should not compile (reordered arguments) // constexpr quantity<metre, si::dim_length, double> error(0); // should not compile (reordered arguments)
// constexpr quantity<si::dim_length, scaled_unit<ratio<-1, 1>, metre>, int> error(0); // should not compile (negative unit ratio) // constexpr quantity<si::dim_length, scaled_unit<ratio(-1, 1), metre>, int> error(0); // should not compile (negative unit ratio)
// member types // member types
@ -138,23 +138,23 @@ static_assert(std::is_same_v<decltype(1.0 * length<metre, int>()), length<metre,
static_assert( static_assert(
std::is_same_v<decltype(speed<metre_per_second, int>() * physical::si::time<second, int>()), length<metre, int>>); std::is_same_v<decltype(speed<metre_per_second, int>() * physical::si::time<second, int>()), length<metre, int>>);
static_assert( static_assert(
std::is_same_v<decltype(speed<metre_per_second, int>() * physical::si::time<hour, int>()), length<scaled_unit<ratio<36, 1, 2>, metre>, int>>); std::is_same_v<decltype(speed<metre_per_second, int>() * physical::si::time<hour, int>()), length<scaled_unit<ratio(36, 1, 2), metre>, int>>);
static_assert(std::is_same_v<decltype(length<metre>() * physical::si::time<minute>()), static_assert(std::is_same_v<decltype(length<metre>() * physical::si::time<minute>()),
quantity<unknown_dimension<units::exp<dim_length, 1>, units::exp<dim_time, 1>>, scaled_unit<ratio<6, 1, 1>, unknown_coherent_unit>>>); quantity<unknown_dimension<units::exp<dim_length, 1>, units::exp<dim_time, 1>>, scaled_unit<ratio(6, 1, 1), unknown_coherent_unit>>>);
static_assert(std::is_same_v<decltype(1 / physical::si::time<second, int>()), frequency<hertz, int>>); static_assert(std::is_same_v<decltype(1 / physical::si::time<second, int>()), frequency<hertz, int>>);
static_assert(std::is_same_v<decltype(1 / physical::si::time<minute, int>()), frequency<scaled_unit<ratio<1, 6, -1>, hertz>, int>>); static_assert(std::is_same_v<decltype(1 / physical::si::time<minute, int>()), frequency<scaled_unit<ratio(1, 6, -1), hertz>, int>>);
static_assert(std::is_same_v<decltype(1 / frequency<hertz, int>()), physical::si::time<second, int>>); static_assert(std::is_same_v<decltype(1 / frequency<hertz, int>()), physical::si::time<second, int>>);
static_assert(std::is_same_v<decltype(1 / length<kilometre>()), static_assert(std::is_same_v<decltype(1 / length<kilometre>()),
quantity<unknown_dimension<units::exp<dim_length, -1>>, scaled_unit<ratio<1, 1, -3>, unknown_coherent_unit>>>); quantity<unknown_dimension<units::exp<dim_length, -1>>, scaled_unit<ratio(1, 1, -3), unknown_coherent_unit>>>);
static_assert(std::is_same_v<decltype(length<metre, int>() / 1.0), length<metre, double>>); static_assert(std::is_same_v<decltype(length<metre, int>() / 1.0), length<metre, double>>);
static_assert(std::is_same_v<decltype(length<metre, int>() / length<metre, double>()), double>); static_assert(std::is_same_v<decltype(length<metre, int>() / length<metre, double>()), double>);
static_assert(std::is_same_v<decltype(length<kilometre, int>() / length<metre, double>()), double>); static_assert(std::is_same_v<decltype(length<kilometre, int>() / length<metre, double>()), double>);
static_assert( static_assert(
std::is_same_v<decltype(length<metre, int>() / physical::si::time<second, int>()), speed<metre_per_second, int>>); std::is_same_v<decltype(length<metre, int>() / physical::si::time<second, int>()), speed<metre_per_second, int>>);
static_assert( static_assert(
std::is_same_v<decltype(length<metre>() / physical::si::time<minute>()), speed<scaled_unit<ratio<1, 6, -1>, metre_per_second>>>); std::is_same_v<decltype(length<metre>() / physical::si::time<minute>()), speed<scaled_unit<ratio(1, 6, -1), metre_per_second>>>);
static_assert(std::is_same_v<decltype(physical::si::time<minute>() / length<metre>()), static_assert(std::is_same_v<decltype(physical::si::time<minute>() / length<metre>()),
quantity<unknown_dimension<units::exp<dim_length, -1>, units::exp<dim_time, 1>>, scaled_unit<ratio<6 ,1 , 1>, unknown_coherent_unit>>>); quantity<unknown_dimension<units::exp<dim_length, -1>, units::exp<dim_time, 1>>, scaled_unit<ratio(6 ,1 , 1), unknown_coherent_unit>>>);
static_assert(std::is_same_v<decltype(length<metre, int>() % short(1)), length<metre, int>>); static_assert(std::is_same_v<decltype(length<metre, int>() % short(1)), length<metre, int>>);
static_assert(std::is_same_v<decltype(length<metre, int>() % length<metre, short>(1)), length<metre, int>>); static_assert(std::is_same_v<decltype(length<metre, int>() % length<metre, short>(1)), length<metre, int>>);
@ -234,7 +234,7 @@ static_assert(std::equality_comparable_with<decltype(1q_m), decltype(1q_ft_us)>)
// quantity_cast // quantity_cast
static_assert(std::is_same_v<decltype(quantity_cast<scaled_unit<ratio<1>, metre>>(2q_km))::unit, metre>); static_assert(std::is_same_v<decltype(quantity_cast<scaled_unit<ratio(1), metre>>(2q_km))::unit, metre>);
static_assert(quantity_cast<length<metre, int>>(2q_km).count() == 2000); static_assert(quantity_cast<length<metre, int>>(2q_km).count() == 2000);
static_assert(quantity_cast<length<kilometre, int>>(2000q_m).count() == 2); static_assert(quantity_cast<length<kilometre, int>>(2000q_m).count() == 2);

View File

@ -1,110 +1,92 @@
// The MIT License (MIT) // The MIT License (MIT)
// //
// Copyright (c) 2018 Mateusz Pusz // Copyright (c) 2018 Mateusz Pusz
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#include "units/ratio.h" #include "units/ratio.h"
namespace { namespace {
using namespace units; using namespace units;
template<Ratio R1, Ratio R2> static_assert(ratio(2, 4) == ratio(1, 2));
inline constexpr bool same = R1::num == R2::num && R1::den == R2::den && R1::exp == R2::exp;
static_assert(same<ratio<2, 4>, ratio<1, 2>>); // basic exponents tests
// note use of ::type is required because template params are changed while stamping out template
static_assert(ratio(2, 40, 1) == ratio(1, 20, 1));
static_assert(ratio(20, 4, -1) == ratio(10, 2, -1));
static_assert(ratio(200, 5) == ratio(20'000, 50, -1));
// basic exponents tests static_assert(ratio(1) * ratio(3, 8) == ratio(3, 8));
// note use of ::type is required because template params are changed while stamping out template static_assert(ratio(3, 8) * ratio(1) == ratio(3, 8));
static_assert(std::is_same_v<ratio<2, 40, 1>::type, ratio<1, 20, 1>::type>); static_assert(ratio(4) * ratio(1, 8) == ratio(1, 2));
static_assert(std::is_same_v<ratio<20, 4, -1>::type, ratio<10, 2, -1>::type>); static_assert(ratio(4) * ratio(1, 2) == ratio(2));
static_assert(std::is_same_v<ratio<200, 5>::type, ratio<20'000, 50, -1>::type>); static_assert(ratio(1, 8) * ratio(2) == ratio(1, 4));
static_assert(ratio(1, 2) * ratio(8) == ratio(4));
static_assert(std::is_same_v<ratio_multiply<ratio<1>, ratio<3, 8>>, ratio<3, 8>>); // multiply with exponents
static_assert(std::is_same_v<ratio_multiply<ratio<3, 8>, ratio<1>>, ratio<3, 8>>); static_assert(ratio(1, 8, 2) * ratio(2, 1, 4) == ratio(1, 4, 6));
static_assert(std::is_same_v<ratio_multiply<ratio<4>, ratio<1, 8>>, ratio<1, 2>>); static_assert(ratio(1, 2, -4) * ratio(8, 1, 3) == ratio(4, 1, -1));
static_assert(std::is_same_v<ratio_multiply<ratio<4>, ratio<1, 2>>, ratio<2>>);
static_assert(std::is_same_v<ratio_multiply<ratio<1, 8>, ratio<2>>, ratio<1, 4>>);
static_assert(std::is_same_v<ratio_multiply<ratio<1, 2>, ratio<8>>, ratio<4>>);
// multiply with exponents static_assert(ratio(4) / ratio(2) == ratio(2));
static_assert(std::is_same_v<ratio_multiply<ratio<1, 8, 2>, ratio<2, 1, 4>>, ratio<1, 4, 6>>); static_assert(ratio(2) / ratio(8) == ratio(1, 4));
static_assert(std::is_same_v<ratio_multiply<ratio<1, 2, -4>, ratio<8, 1, 3>>, ratio<4, 1, -1>>); static_assert(ratio(1, 8) / ratio(2) == ratio(1, 16));
static_assert(ratio(6) / ratio(3) == ratio(2));
static_assert(std::is_same_v<ratio_divide<ratio<4>, ratio<2>>, ratio<2>>); // divide with exponents
static_assert(std::is_same_v<ratio_divide<ratio<2>, ratio<8>>, ratio<1, 4>>); static_assert(ratio(1, 8, -6) / ratio(2, 1, -8) == ratio(1, 16, 2));
static_assert(std::is_same_v<ratio_divide<ratio<1, 8>, ratio<2>>, ratio<1, 16>>); static_assert(ratio(6, 1, 4) / ratio(3) == ratio(2, 1, 4));
static_assert(std::is_same_v<ratio_divide<ratio<6>, ratio<3>>, ratio<2>>);
// divide with exponents static_assert(pow<0>(ratio(2)) == ratio(1));
static_assert(std::is_same_v<ratio_divide<ratio<1, 8, -6>, ratio<2, 1, -8>>, ratio<1, 16, 2>>); static_assert(pow<1>(ratio(2)) == ratio(2));
static_assert(std::is_same_v<ratio_divide<ratio<6, 1, 4>, ratio<3>>, ratio<2, 1, 4>>); static_assert(pow<2>(ratio(2)) == ratio(4));
static_assert(pow<3>(ratio(2)) == ratio(8));
static_assert(pow<0>(ratio(1, 2)) == ratio(1));
static_assert(pow<1>(ratio(1, 2)) == ratio(1, 2));
static_assert(pow<2>(ratio(1, 2)) == ratio(1, 4));
static_assert(pow<3>(ratio(1, 2)) == ratio(1, 8));
static_assert(std::is_same_v<ratio_pow<ratio<2>, 0>, ratio<1>>); // pow with exponents
static_assert(std::is_same_v<ratio_pow<ratio<2>, 1>, ratio<2>>); static_assert(pow<2>(ratio(1, 2, 3)) == ratio(1, 4, 6));
static_assert(std::is_same_v<ratio_pow<ratio<2>, 2>, ratio<4>>); static_assert(pow<3>(ratio(1, 2, -6)) == ratio(1, 8, -18));
static_assert(std::is_same_v<ratio_pow<ratio<2>, 3>, ratio<8>>);
static_assert(std::is_same_v<ratio_pow<ratio<1, 2>, 0>, ratio<1>>);
static_assert(std::is_same_v<ratio_pow<ratio<1, 2>, 1>, ratio<1, 2>>);
static_assert(std::is_same_v<ratio_pow<ratio<1, 2>, 2>, ratio<1, 4>>);
static_assert(std::is_same_v<ratio_pow<ratio<1, 2>, 3>, ratio<1, 8>>);
// pow with exponents static_assert(sqrt(ratio(9)) == ratio(3));
static_assert(std::is_same_v<ratio_pow<ratio<1, 2, 3>, 2>, ratio<1, 4, 6>>); static_assert(sqrt(ratio(4)) == ratio(2));
static_assert(std::is_same_v<ratio_pow<ratio<1, 2, -6>, 3>, ratio<1, 8, -18>>); static_assert(sqrt(ratio(1)) == ratio(1));
static_assert(sqrt(ratio(0)) == ratio(0));
static_assert(sqrt(ratio(1, 4)) == ratio(1, 2));
static_assert(std::is_same_v<ratio_sqrt<ratio<9>>, ratio<3>>); // sqrt with exponents
static_assert(std::is_same_v<ratio_sqrt<ratio<4>>, ratio<2>>); static_assert(sqrt(ratio(9, 1, 2)) == ratio(3, 1, 1));
static_assert(std::is_same_v<ratio_sqrt<ratio<1>>, ratio<1>>); static_assert(sqrt(ratio(4)) == ratio(2));
static_assert(std::is_same_v<ratio_sqrt<ratio<0>>, ratio<0>>);
static_assert(std::is_same_v<ratio_sqrt<ratio<1, 4>>, ratio<1, 2>>);
// sqrt with exponents // common_ratio
static_assert(std::is_same_v<ratio_sqrt<ratio<9, 1, 2>>, ratio<3, 1, 1>>); static_assert(common_ratio(ratio(1), ratio(1000)) == ratio(1));
static_assert(std::is_same_v<ratio_sqrt<ratio<4>>, ratio<2>>); static_assert(common_ratio(ratio(1000), ratio(1)) == ratio(1));
static_assert(common_ratio(ratio(1), ratio(1, 1000)) == ratio(1, 1000));
static_assert(common_ratio(ratio(1, 1000), ratio(1)) == ratio(1, 1000));
static_assert(common_ratio(ratio(100, 1), ratio(10, 1)) == ratio(10, 1));
static_assert(common_ratio(ratio(100, 1), ratio(1, 10)) == ratio(1, 10));
// unused // common ratio with exponents
// static_assert(std::is_same_v<units::ratio_add<units::ratio<1, 2, 0>, units::ratio<1, 3, 0>>, units::ratio<5, 6, 0>>); static_assert(common_ratio(ratio(1), ratio(1, 1, 3)) == ratio(1));
// static_assert(std::is_same_v<units::ratio_add<units::ratio<1, 2, 1>, units::ratio<1, 3, 1>>, units::ratio<5, 6, 1>>); static_assert(common_ratio(ratio(10, 1, -1), ratio(1, 1, -3)) == ratio(1, 1, -3));
// static_assert(std::is_same_v<units::ratio_add<units::ratio<3, 8, 2>, units::ratio<2, 7, 2>>, units::ratio<37, 56, 2>>);
// static_assert(std::is_same_v<units::ratio_add<units::ratio<3, 8, 2>, units::ratio<2, 7, 1>>, units::ratio<226, 56, 1>>); } // namespace
// static_assert(std::is_same_v<units::ratio_add<units::ratio<2, 7, 1>, units::ratio<3, 8, 2>>, units::ratio<226, 56, 1>>);
// static_assert(std::is_same_v<units::ratio_add<units::ratio<3, 8, -2>, units::ratio<2, 7, -1>>, units::ratio<181, 56, -2>>);
// static_assert(std::is_same_v<units::ratio_add<units::ratio<2, 7, -1>, units::ratio<3, 8, -2>>, units::ratio<181, 56, -2>>);
// common_ratio
// note use of ::type is required because template params are changed while stamping out template
static_assert(std::is_same_v<common_ratio<ratio<1>::type, ratio<1000>>, ratio<1>::type>);
static_assert(std::is_same_v<common_ratio<ratio<1000>, ratio<1>>::type, ratio<1>::type>);
static_assert(std::is_same_v<common_ratio<ratio<1>, ratio<1, 1000>>::type, ratio<1, 1000>::type>);
static_assert(std::is_same_v<common_ratio<ratio<1, 1000>, ratio<1>>::type, ratio<1, 1000>::type>);
static_assert(std::is_same_v<common_ratio<ratio<100, 1>, ratio<10, 1>>::type, ratio<10, 1>::type>);
static_assert(std::is_same_v<common_ratio<ratio<100, 1>, ratio<1, 10>>::type, ratio<1, 10>::type>);
// common ratio with exponents
static_assert(std::is_same_v<common_ratio<ratio<1>, ratio<1, 1, 3>>::type, ratio<1>::type>);
static_assert(std::is_same_v<common_ratio<ratio<10, 1, -1>, ratio<1, 1, -3>>::type, ratio<1, 1, -3>::type>);
} // namespace

View File

@ -237,7 +237,7 @@ static_assert(kilogray::symbol == "kGy");
// speed // speed
static_assert(std::is_same_v<decltype(1q_km / 1q_s), speed<scaled_unit<ratio<1, 1, 3>, metre_per_second>, std::int64_t>>); static_assert(std::is_same_v<decltype(1q_km / 1q_s), speed<scaled_unit<ratio(1, 1, 3), metre_per_second>, std::int64_t>>);
static_assert(10q_m / 5q_s == 2q_m_per_s); static_assert(10q_m / 5q_s == 2q_m_per_s);
static_assert(10 / 5q_s * 1q_m == 2q_m_per_s); static_assert(10 / 5q_s * 1q_m == 2q_m_per_s);

View File

@ -31,12 +31,12 @@ using namespace units::physical;
struct metre : named_unit<metre, "m", si::prefix> {}; struct metre : named_unit<metre, "m", si::prefix> {};
struct centimetre : prefixed_unit<centimetre, si::centi, metre> {}; struct centimetre : prefixed_unit<centimetre, si::centi, metre> {};
struct kilometre : prefixed_unit<kilometre, si::kilo, metre> {}; struct kilometre : prefixed_unit<kilometre, si::kilo, metre> {};
struct yard : named_scaled_unit<yard, "yd", no_prefix, ratio<9'144, 1, -4>, metre> {}; struct yard : named_scaled_unit<yard, "yd", no_prefix, ratio(9'144, 1, -4), metre> {};
struct foot : named_scaled_unit<foot, "ft", no_prefix, ratio<1, 3>, yard> {}; struct foot : named_scaled_unit<foot, "ft", no_prefix, ratio(1, 3), yard> {};
struct dim_length : base_dimension<"length", metre> {}; struct dim_length : base_dimension<"length", metre> {};
struct second : named_unit<second, "s", si::prefix> {}; struct second : named_unit<second, "s", si::prefix> {};
struct hour : named_scaled_unit<hour, "h", no_prefix, ratio<36, 1, 2>, second> {}; struct hour : named_scaled_unit<hour, "h", no_prefix, ratio(36, 1, 2), second> {};
struct dim_time : base_dimension<"time", second> {}; struct dim_time : base_dimension<"time", second> {};
struct kelvin : named_unit<kelvin, "K", no_prefix> {}; struct kelvin : named_unit<kelvin, "K", no_prefix> {};
@ -46,11 +46,11 @@ struct metre_per_second : unit<metre_per_second> {};
struct dim_speed : derived_dimension<dim_speed, metre_per_second, units::exp<dim_length, 1>, units::exp<dim_time, -1>> {}; struct dim_speed : derived_dimension<dim_speed, metre_per_second, units::exp<dim_length, 1>, units::exp<dim_time, -1>> {};
struct kilometre_per_hour : deduced_unit<kilometre_per_hour, dim_speed, kilometre, hour> {}; struct kilometre_per_hour : deduced_unit<kilometre_per_hour, dim_speed, kilometre, hour> {};
static_assert(std::is_same_v<downcast<scaled_unit<ratio<1>, metre>>, metre>); static_assert(std::is_same_v<downcast<scaled_unit<ratio(1), metre>>, metre>);
static_assert(std::is_same_v<downcast<scaled_unit<ratio<1, 1, -2>, metre>>, centimetre>); static_assert(std::is_same_v<downcast<scaled_unit<ratio(1, 1, -2), metre>>, centimetre>);
static_assert(std::is_same_v<downcast<scaled_unit<ratio<yard::ratio::num, yard::ratio::den, yard::ratio::exp>, metre>>, yard>); static_assert(std::is_same_v<downcast<scaled_unit<ratio(yard::ratio.num, yard::ratio.den, yard::ratio.exp), metre>>, yard>);
static_assert(std::is_same_v<downcast<scaled_unit<ratio_multiply<typename yard::ratio, ratio<1, 3>>, metre>>, foot>); static_assert(std::is_same_v<downcast<scaled_unit<yard::ratio * ratio(1, 3), metre>>, foot>);
static_assert(std::is_same_v<downcast<scaled_unit<ratio_divide<typename kilometre::ratio, typename hour::ratio>, metre_per_second>>, kilometre_per_hour>); static_assert(std::is_same_v<downcast<scaled_unit<kilometre::ratio / hour::ratio, metre_per_second>>, kilometre_per_hour>);
static_assert(centimetre::symbol == "cm"); static_assert(centimetre::symbol == "cm");
static_assert(kilometre::symbol == "km"); static_assert(kilometre::symbol == "km");