18#include <system_error>
21#ifndef CONFIG_ELECTRO_STD_FORMAT
22#if __has_include(<format>) && defined(__cpp_lib_format)
23#define CONFIG_ELECTRO_STD_FORMAT 1
25#define CONFIG_ELECTRO_STD_FORMAT 0
29#if CONFIG_ELECTRO_STD_FORMAT
42#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ >= 14
43#define ELECTRO_HAS_UTF8_LITERALS 1
45#define ELECTRO_HAS_UTF8_LITERALS 0
128template<
typename Unit,
typename Rep,
typename Precision = std::ratio<1>>
138template<
typename Unit,
typename Rep,
typename Precision>
145template<
typename Rep>
151template<
typename Rep,
typename Period>
170template<
typename Rep>
179 static constexpr Rep min()
noexcept {
return std::numeric_limits<Rep>::lowest(); }
183struct _is_ratio : std::false_type {};
185template<std::
intmax_t Num, std::
intmax_t Denom>
186struct _is_ratio<
std::
ratio<Num, Denom>> : std::true_type {};
229template<
typename R1,
typename R2>
233 return (R1::den /
g2) * (R2::num /
g1);
236template<
typename From,
typename To>
254 static constexpr const char*
symbol =
"V";
259 static constexpr const char*
symbol =
"A";
264 static constexpr const char*
symbol =
"Ω";
269 static constexpr const char*
symbol =
"W";
274 static constexpr const char*
symbol =
"C";
279 static constexpr const char*
symbol =
"J";
284 static constexpr const char*
symbol =
"F";
289 static constexpr const char*
symbol =
"H";
323template<
typename Unit,
typename Rep,
typename Precision>
326 static_assert(_is_ratio<Precision>::value,
"precision must be a specialization of std::ratio");
327 static_assert(Precision::num > 0,
"precision must be positive");
350 template<
typename Rep2>
353 : _r(
static_cast<rep>(
r)) {}
366 template<
typename Rep2,
typename Precision2>
367 requires std::convertible_to<const Rep2&, rep> &&
379 template<
typename Rep2,
typename Precision2>
475template<
typename ToQuantity,
typename Unit,
typename Rep,
typename Precision>
478 std::is_same_v<typename ToQuantity::unit, Unit>,
"quantity_cast cannot convert between different units"
480 if constexpr (std::is_same_v<ToQuantity, quantity<Unit, Rep, Precision>>) {
483 using to_rep =
typename ToQuantity::rep;
485 using cf = std::ratio_divide<Precision, to_precision>;
488 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
489#ifdef __SIZEOF_INT128__
490 using cr = std::common_type_t<to_rep, Rep, intmax_t>;
496 if constexpr (cf::den == 1 && cf::num == 1) {
498 }
else if constexpr (cf::den == 1) {
501 }
else if constexpr (cf::num == 1) {
516 using cr = std::common_type_t<to_rep, Rep, intmax_t>;
517 if constexpr (cf::den == 1 && cf::num == 1) {
519 }
else if constexpr (cf::den == 1) {
521 }
else if constexpr (cf::num == 1) {
526 static_cast<cr>(
q.count()) *
static_cast<cr>(cf::num) /
static_cast<cr>(cf::den)
551template<
typename ToQuantity,
typename Unit,
typename Rep,
typename Precision>
553 using to_rep =
typename ToQuantity::rep;
556 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
582template<
typename ToQuantity,
typename Unit,
typename Rep,
typename Precision>
584 using to_rep =
typename ToQuantity::rep;
587 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
613template<
typename ToQuantity,
typename Unit,
typename Rep,
typename Precision>
615 using to_rep =
typename ToQuantity::rep;
617 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
647template<
typename Unit,
typename Rep,
typename Precision>
657template<
typename Unit,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
665template<
typename Unit,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
673template<
typename Unit,
typename Rep1,
typename Precision,
typename Rep2>
674 requires not_quantity<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
678 return cq(
cq(
q).count() *
r);
682template<
typename Unit,
typename Rep1,
typename Rep2,
typename Precision>
683 requires not_quantity<Rep1> && std::convertible_to<const Rep1&, std::common_type_t<Rep1, Rep2>>
690template<
typename Unit,
typename Rep1,
typename Precision,
typename Rep2>
691 requires not_quantity<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
695 return cq(
cq(
q).count() /
s);
699template<
typename Unit,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
701 -> std::common_type_t<Rep1, Rep2> {
707template<
typename Unit,
typename Rep1,
typename Precision,
typename Rep2>
708 requires not_quantity<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>> &&
713 return cq(
cq(
q).count() %
s);
717template<
typename Unit,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
725template<
typename Unit,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
731template<
typename Unit,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
732 requires std::three_way_comparable<std::common_type_t<Rep1, Rep2>>
746template<
typename ToUnit,
typename U1,
typename R1,
typename P1,
typename U2,
typename R2,
typename P2>
748 using cr = std::common_type_t<R1, R2>;
755template<
typename ToUnit,
typename U1,
typename R1,
typename P1,
typename U2,
typename R2,
typename P2>
757 using cr = std::common_type_t<R1, R2>;
763template<
typename ToUnit,
typename U1,
typename R1,
typename P1, duration_like D>
765 using cr = std::common_type_t<R1, typename D::rep>;
771template<
typename ToUnit,
typename U1,
typename R1,
typename P1, duration_like D>
773 using cr = std::common_type_t<R1, typename D::rep>;
779template<
typename U1,
typename R1,
typename P1,
typename U2,
typename R2,
typename P2>
781 using cr = std::common_type_t<R1, R2>;
782 using result_t = std::chrono::duration<cr, std::ratio_divide<P1, P2>>;
787template<
typename U1,
typename R1,
typename P1,
typename U2,
typename R2,
typename P2>
789 using cr = std::common_type_t<R1, R2>;
790 using result_t = std::chrono::duration<cr, std::ratio_multiply<P1, P2>>;
807template<
typename R1,
typename P1,
typename R2,
typename P2>
813template<
typename R1,
typename P1,
typename R2,
typename P2>
819template<
typename R1,
typename P1,
typename R2,
typename P2>
825template<
typename R1,
typename P1,
typename R2,
typename P2>
831template<
typename R1,
typename P1,
typename R2,
typename P2>
837template<
typename R1,
typename P1,
typename R2,
typename P2>
843template<
typename R1,
typename P1,
typename R2,
typename P2>
849template<
typename R1,
typename P1,
typename R2,
typename P2>
861template<
typename R1,
typename P1, duration_like D>
867template<duration_like D,
typename R1,
typename P1>
873template<
typename R1,
typename P1, duration_like D>
885template<
typename R1,
typename P1,
typename R2,
typename P2>
897template<
typename R1,
typename P1, duration_like D>
903template<duration_like D,
typename R1,
typename P1>
909template<
typename R1,
typename P1, duration_like D>
915template<
typename R1,
typename P1,
typename R2,
typename P2>
927template<
typename R1,
typename P1,
typename R2,
typename P2>
933template<
typename R1,
typename P1,
typename R2,
typename P2>
939template<
typename R1,
typename P1,
typename R2,
typename P2>
945template<
typename R1,
typename P1,
typename R2,
typename P2>
951template<
typename R1,
typename P1,
typename R2,
typename P2>
957template<
typename R1,
typename P1,
typename R2,
typename P2>
969template<
typename R1,
typename P1,
typename R2,
typename P2>
975template<
typename R1,
typename P1,
typename R2,
typename P2>
987template<
typename R1,
typename P1,
typename R2,
typename P2>
1008template<
typename Rep,
typename Precision = std::ratio<1>>
1012template<
typename Rep,
typename Precision = std::ratio<1>>
1016template<
typename Rep,
typename Precision = std::ratio<1>>
1020template<
typename Rep,
typename Precision = std::ratio<1>>
1024template<
typename Rep,
typename Precision = std::ratio<1>>
1028template<
typename Rep,
typename Precision = std::ratio<1>>
1032template<
typename Rep,
typename Precision = std::ratio<1>>
1036template<
typename Rep,
typename Precision = std::ratio<1>>
1141template<
typename Precision>
1147 static constexpr const char* symbol =
"f";
1152 static constexpr const char* symbol =
"p";
1157 static constexpr const char* symbol =
"n";
1162 static constexpr const char* symbol =
"µ";
1167 static constexpr const char* symbol =
"m";
1172 static constexpr const char* symbol =
"";
1177 static constexpr const char* symbol =
"k";
1182 static constexpr const char* symbol =
"M";
1187 static constexpr const char* symbol =
"G";
1192 static constexpr const char* symbol =
"T";
1207template<
typename Unit,
typename Precision>
1219struct quantity_suffix<coulomb_unit,
std::
ratio<18, 5>> {
1220 static std::string
value() {
return "mAh"; }
1224struct quantity_suffix<coulomb_unit,
std::
ratio<3600>> {
1225 static std::string
value() {
return "Ah"; }
1229struct quantity_suffix<joule_unit,
std::
ratio<3600>> {
1230 static std::string
value() {
return "Wh"; }
1234struct quantity_suffix<joule_unit,
std::
ratio<3600000>> {
1235 static std::string
value() {
return "kWh"; }
1238template<
typename Unit,
typename Precision>
1245template<
typename Unit,
typename Precision>
1247 using scale = std::ratio<1>;
1248 static constexpr const char*
suffix = Unit::symbol;
1252 using scale = std::ratio<3600>;
1253 static constexpr const char*
suffix =
"Ah";
1257 using scale = std::ratio<3600>;
1258 static constexpr const char*
suffix =
"Wh";
1280template<
typename Unit,
typename Rep,
typename Precision>
1284 using upc =
typename std::ratio_divide<typename Precision::type, typename display::scale>::type;
1285 double v =
static_cast<double>(
q.count());
1286 if constexpr (upc::num != 1) {
1287 v *=
static_cast<double>(upc::num);
1289 if constexpr (upc::den != 1) {
1290 v /=
static_cast<double>(upc::den);
1292 return {
v, display::suffix};
1296 while (
n >= 10 &&
n % 10 == 0) {
1312template<
typename Precision>
1316template<
typename Precision>
1320constexpr double _pow10(
int k) {
1322 for (
int i = 0;
i < (
k < 0 ? -
k :
k); ++
i) {
1341 {-15, si_prefix<std::femto>::symbol},
1342 {-12, si_prefix<std::pico>::symbol},
1343 {-9, si_prefix<std::nano>::symbol},
1344 {-6, si_prefix<std::micro>::symbol},
1345 {-3, si_prefix<std::milli>::symbol},
1347 {3, si_prefix<std::kilo>::symbol},
1348 {6, si_prefix<std::mega>::symbol},
1349 {9, si_prefix<std::giga>::symbol},
1350 {12, si_prefix<std::tera>::symbol},
1359template<
typename Unit,
typename Rep,
typename Precision>
1361 using p =
typename Precision::type;
1362 const double count =
static_cast<double>(
q.count());
1364 if (count == 0 || !std::isfinite(count)) {
1368 for (std::size_t
i =
n;
i-- > 1;) {
1370 if (
v >= 1.0 ||
v <= -1.0) {
1387template<std::
floating_po
int T>
1389 std::string
s(32,
'\0');
1391 const auto result = std::to_chars(
s.data(),
s.data() +
s.size(),
v, std::chars_format::fixed);
1392 if (
result.ec == std::errc()) {
1393 s.resize(
static_cast<std::size_t
>(
result.ptr -
s.data()));
1396 s.resize(
s.size() * 2);
1402template<
typename Rep>
1404 if constexpr (std::floating_point<Rep>) {
1407 return std::to_string(
r);
1412template<
typename OutputIt>
1414 for (; *
s !=
'\0'; ++
s) {
1488template<
typename Unit,
typename Rep,
typename Precision>
1504template<
typename Unit,
typename Rep,
typename Precision>
1521template<
typename Unit,
typename Rep,
typename Precision>
1532template<
typename Unit,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
1535 using common_precision = std::ratio<
1537 (Precision1::den /
electro::_gcd(Precision1::den, Precision2::den)) * Precision2::den>;
1543#if CONFIG_ELECTRO_STD_FORMAT
1567template<
typename Unit,
typename Rep,
typename Precision>
1569struct formatter<
electro::quantity<Unit, Rep, Precision>, char> {
1571 enum class _unit_form { own, base, scaled };
1573 std::formatter<double> _num;
1574 bool _has_spec =
false;
1575 _unit_form _form = _unit_form::own;
1578 constexpr auto parse(format_parse_context& ctx) {
1579 auto it = ctx.begin();
1580 if (it != ctx.end() && (*it ==
'b' || *it ==
's')) {
1581 _form = *it ==
'b' ? _unit_form::base : _unit_form::scaled;
1582 ctx.advance_to(++it);
1584 if (it == ctx.end() || *it ==
'}') {
1588 return _num.parse(ctx);
1591 template<
typename FormatContext>
1593 if (_form != _unit_form::own) {
1596 auto out = _has_spec ? _num.format(d.value, ctx)
1598 return std::format_to(out,
"{}", d.suffix);
1602 auto out = _num.format(
static_cast<double>(q.
count()), ctx);
1603 return std::format_to(out,
"{}", suffix);
1605 if constexpr (std::floating_point<Rep>) {
1608 return std::format_to(ctx.out(),
"{}{}", q.
count(), suffix);
1623template<
unsigned long long Value,
unsigned long long Power>
1625 static constexpr unsigned long long value = 10 * pow10<Value, Power - 1>::value;
1628template<
unsigned long long Value>
1629struct pow10<Value, 0> {
1630 static constexpr unsigned long long value = Value;
1633template<
char... Digits>
1636template<
char D,
char... Rest>
1637struct parse_int<D, Rest...> {
1638 static_assert(D >=
'0' && D <=
'9',
"invalid digit");
1639 static constexpr unsigned long long value = pow10<D -
'0',
sizeof...(Rest)>::value + parse_int<Rest...>::value;
1643struct parse_int<D> {
1644 static_assert(D >=
'0' && D <=
'9',
"invalid digit");
1645 static constexpr unsigned long long value = D -
'0';
1648template<
typename Quantity,
char... Digits>
1649constexpr Quantity check_overflow() {
1650 using parsed = parse_int<Digits...>;
1651 constexpr typename Quantity::rep repval = parsed::value;
1653 repval >= 0 &&
static_cast<unsigned long long>(repval) == parsed::value,
1654 "literal value cannot be represented by quantity type"
1656 return Quantity(repval);
1663template<
char... Digits>
1669template<
char... Digits>
1675template<
char... Digits>
1681template<
char... Digits>
1687template<
char... Digits>
1693template<
char... Digits>
1699template<
char... Digits>
1705template<
char... Digits>
1711template<
char... Digits>
1717template<
char... Digits>
1723template<
char... Digits>
1728#if ELECTRO_HAS_UTF8_LITERALS
1730template<
char... Digits>
1736template<
char... Digits>
1742template<
char... Digits>
1748template<
char... Digits>
1755template<
char... Digits>
1761template<
char... Digits>
1767template<
char... Digits>
1773template<
char... Digits>
1779template<
char... Digits>
1785template<
char... Digits>
1791template<
char... Digits>
1797template<
char... Digits>
1803template<
char... Digits>
1809template<
char... Digits>
1815template<
char... Digits>
1821template<
char... Digits>
1827template<
char... Digits>
1833template<
char... Digits>
1839template<
char... Digits>
1845template<
char... Digits>
1851template<
char... Digits>
1857template<
char... Digits>
1863template<
char... Digits>
1869template<
char... Digits>
1875template<
char... Digits>
A quantity of an electrical unit with a representation and precision.
Unit unit
The unit tag type.
constexpr quantity operator--(int)
constexpr quantity & operator*=(const rep &r)
constexpr quantity operator++(int)
constexpr quantity & operator++()
constexpr quantity & operator+=(const quantity &q)
Rep rep
The representation type.
static constexpr quantity zero() noexcept
Returns a zero quantity.
constexpr quantity()=default
Constructs a zero quantity.
constexpr quantity & operator/=(const rep &r)
static constexpr quantity max() noexcept
Returns the maximum representable quantity.
constexpr quantity< Unit, typename std::common_type< rep >::type, precision > operator+() const
constexpr quantity & operator--()
constexpr quantity< Unit, typename std::common_type< rep >::type, precision > operator-() const
quantity & operator=(const quantity &)=default
quantity(const quantity &)=default
typename Precision::type precision
The precision as a std::ratio.
constexpr rep count() const
Returns the tick count.
constexpr quantity & operator-=(const quantity &q)
constexpr quantity & operator%=(const rep &r)
static constexpr quantity min() noexcept
Returns the minimum representable quantity.
constexpr quantity(const quantity< Unit, Rep2, Precision2 > &q)
Explicit constructor for lossy precision conversions.
constexpr quantity & operator%=(const quantity &q)
constexpr quantity(const Rep2 &r)
Constructs from a tick count.
capacitance< int64_t, std::pico > picofarads
Capacitance with 1 pF precision.
resistance< int64_t, std::milli > milliohms
Resistance with 1 mΩ precision.
inductance< int64_t, std::milli > millihenries
Inductance with 1 mH precision.
capacitance< int64_t > farads
Capacitance with 1 F precision.
capacitance< int64_t, std::nano > nanofarads
Capacitance with 1 nF precision.
energy< int64_t, std::ratio< 3600000 > > kilowatt_hours
Energy with 1 kWh (3,600,000 J) precision.
power< int64_t, std::mega > megawatts
Power with 1 MW precision.
inductance< int64_t, std::micro > microhenries
Inductance with 1 µH precision.
charge< int64_t, std::ratio< 18, 5 > > milliampere_hours
Charge with 1 mAh (3.6 C) precision.
voltage< int64_t, std::milli > millivolts
Voltage with 1 mV precision.
energy< int64_t, std::kilo > kilojoules
Energy with 1 kJ precision.
power< int64_t > watts
Power with 1 W precision.
energy< int64_t, std::milli > millijoules
Energy with 1 mJ precision.
voltage< int64_t, std::kilo > kilovolts
Voltage with 1 kV precision.
capacitance< int64_t, std::micro > microfarads
Capacitance with 1 µF precision.
current< int64_t, std::milli > milliamperes
Current with 1 mA precision.
current< int64_t > amperes
Current with 1 A precision.
resistance< int64_t > ohms
Resistance with 1 Ω precision.
voltage< int64_t, std::micro > microvolts
Voltage with 1 µV precision.
inductance< int64_t, std::nano > nanohenries
Inductance with 1 nH precision.
voltage< int64_t > volts
Voltage with 1 V precision.
charge< int64_t > coulombs
Charge with 1 C precision.
power< int64_t, std::kilo > kilowatts
Power with 1 kW precision.
inductance< int64_t > henries
Inductance with 1 H precision.
energy< int64_t, std::ratio< 3600 > > watt_hours
Energy with 1 Wh (3600 J) precision.
current< int64_t, std::micro > microamperes
Current with 1 µA precision.
power< int64_t, std::micro > microwatts
Power with 1 µW precision.
resistance< int64_t, std::kilo > kiloohms
Resistance with 1 kΩ precision.
energy< int64_t > joules
Energy with 1 J precision.
resistance< int64_t, std::mega > megaohms
Resistance with 1 MΩ precision.
charge< int64_t, std::ratio< 3600 > > ampere_hours
Charge with 1 Ah (3600 C) precision.
power< int64_t, std::milli > milliwatts
Power with 1 mW precision.
User-defined literals for electrical quantity types.
Electrical unit types and utilities.
constexpr quantity< Unit, Rep, Precision > abs(const quantity< Unit, Rep, Precision > &q)
Returns the absolute value of a quantity.
constexpr ToQuantity quantity_cast(const quantity< Unit, Rep, Precision > &q)
Converts a quantity to a different precision or representation.
constexpr auto operator+(const level< Reference, Rep1, Precision1 > &l, const quantity< decibel_unit, Rep2, Precision2 > &g)
Applies a gain to a level.
constexpr auto operator-(const level< Reference, Rep1, Precision1 > &l, const quantity< decibel_unit, Rep2, Precision2 > &g)
Applies a loss to a level.
constexpr auto operator<=>(const level< Reference, Rep1, Precision1 > &a, const level< Reference, Rep2, Precision2 > &b)
constexpr base_unit_t base_unit
Selects the base-unit form in to_string().
constexpr ToLevel ceil(const level< Reference, Rep, Precision > &l)
Converts a level to the target type, rounding toward positive infinity.
constexpr scaled_t scaled
Selects the scaled (engineering) form in to_string().
constexpr auto operator*(const quantity< Unit, Rep1, Precision > &q, const Rep2 &r) -> quantity< Unit, std::common_type_t< Rep1, Rep2 >, Precision >
Multiplies a quantity by a scalar.
constexpr bool treat_as_inexact_v
constexpr ToLevel round(const level< Reference, Rep, Precision > &l)
Converts a level to the target type, rounding to nearest (ties to even).
constexpr auto operator/(const quantity< Unit, Rep1, Precision > &q, const Rep2 &s) -> quantity< Unit, std::common_type_t< Rep1, Rep2 >, Precision >
Divides a quantity by a scalar.
constexpr bool is_quantity_v
constexpr ToLevel floor(const level< Reference, Rep, Precision > &l)
Converts a level to the target type, rounding toward negative infinity.
std::string to_string(const quantity< decibel_unit, Rep, Precision > &g)
Converts a gain to a string, e.g.
constexpr bool operator==(const level< Reference, Rep1, Precision1 > &a, const level< Reference, Rep2, Precision2 > &b)
Electric current, measured in amperes.
static constexpr const char * symbol
Tag type selecting the base-unit form, see base_unit.
Electric charge, measured in coulombs.
static constexpr const char * symbol
Capacitance, measured in farads.
static constexpr const char * symbol
Inductance, measured in henries.
static constexpr const char * symbol
Trait to detect quantity specializations.
Energy, measured in joules.
static constexpr const char * symbol
Electrical resistance, measured in ohms.
static constexpr const char * symbol
Trait giving the display suffix for a (unit, precision) pair.
static std::string value()
Returns the display suffix.
Provides special values for quantity representations.
static constexpr Rep max() noexcept
Returns the maximum representable quantity.
static constexpr Rep min() noexcept
Returns the minimum representable quantity.
static constexpr Rep zero() noexcept
Returns a zero quantity.
Tag type selecting the scaled form, see scaled.
Trait giving the display symbol for an SI precision prefix.
Trait to indicate floating-point-like behavior.
Voltage, measured in volts.
static constexpr const char * symbol
Power, measured in watts.
static constexpr const char * symbol