quanta 0.1.0
Type-safe units for temperature, frequency, electrical and spatial quantities, modeled after std::chrono
Loading...
Searching...
No Matches
frequency.hpp
Go to the documentation of this file.
1#pragma once
2
8#include <assert.h>
9#include <charconv>
10#include <chrono>
11#include <cmath>
12#include <compare>
13#include <concepts>
14#include <cstddef>
15#include <cstdint>
16#include <cstdlib>
17#include <limits>
18#include <quanta/core.hpp>
19#include <ratio>
20#include <string>
21#include <system_error>
22#include <type_traits>
23
106
107// The shared trait, re-exported for convenience; specialize quanta::treat_as_inexact.
110
111template<typename Rep, typename Precision = std::ratio<1>>
112class frequency;
113
118template<typename T>
119struct is_frequency : std::false_type {};
120
121template<typename Rep, typename Precision>
122struct is_frequency<frequency<Rep, Precision>> : std::true_type {};
123
124template<typename T>
126
128template<typename Rep>
129concept not_frequency = !_is_specialization_of_v<Rep, frequency>;
130
139template<typename T>
140concept duration_like = _is_specialization_of_v<std::remove_cvref_t<T>, std::chrono::duration>;
141
152template<typename T>
153concept distance_like = !duration_like<T> && requires(T t) {
154 { t.count() };
155 typename T::period;
156 typename T::rep;
157};
158
163template<typename Rep>
164struct frequency_values {
166 static constexpr Rep zero() noexcept { return Rep(0); }
167
169 static constexpr Rep max() noexcept { return std::numeric_limits<Rep>::max(); }
170
172 static constexpr Rep min() noexcept { return std::numeric_limits<Rep>::lowest(); }
173};
174
221template<typename Rep, typename Precision>
223 static_assert(!is_frequency<Rep>::value, "rep cannot be a quanta::frequency::frequency");
224 static_assert(_is_ratio<Precision>::value, "precision must be a specialization of std::ratio");
225 static_assert(Precision::num > 0, "precision must be positive");
226
227public:
229 using rep = Rep;
231 using precision = typename Precision::type;
232
234 constexpr frequency() = default;
235 frequency(const frequency&) = default;
236
246 template<typename Rep2>
247 requires std::convertible_to<const Rep2&, rep> && (treat_as_inexact_v<rep> || !treat_as_inexact_v<Rep2>)
248 constexpr explicit frequency(const Rep2& r)
249 : _r(static_cast<rep>(r)) {}
250
264 template<typename Rep2, typename Precision2>
265 requires std::convertible_to<const Rep2&, rep> &&
266 detail::_lossless_conversion<Rep2, Precision2, rep, precision> &&
267 detail::_quantity_castable<frequency<Rep2, Precision2>, frequency>
270
287 template<typename Rep2, typename Precision2>
288 requires(!detail::_lossless_conversion<Rep2, Precision2, rep, precision> ||
289 !std::convertible_to<const Rep2&, rep>) &&
290 detail::_quantity_castable<frequency<Rep2, Precision2>, frequency>
291 constexpr explicit frequency(const frequency<Rep2, Precision2>& f)
292 : _r(frequency_cast<frequency>(f).count()) {}
293
294 ~frequency() = default;
295 frequency& operator=(const frequency&) = default;
296
298 constexpr rep count() const { return _r; }
299
303
307
308 constexpr frequency& operator++() {
309 ++_r;
310 return *this;
311 }
312
313 constexpr frequency operator++(int) { return frequency(_r++); }
314
315 constexpr frequency& operator--() {
316 --_r;
317 return *this;
318 }
319
320 constexpr frequency operator--(int) { return frequency(_r--); }
321
322 constexpr frequency& operator+=(const frequency& f) {
323 _r += f.count();
324 return *this;
325 }
326
327 constexpr frequency& operator-=(const frequency& f) {
328 _r -= f.count();
329 return *this;
330 }
331
332 constexpr frequency& operator*=(const rep& r) {
333 _r *= r;
334 return *this;
335 }
336
337 constexpr frequency& operator/=(const rep& r) {
338 _r /= r;
339 return *this;
340 }
341
342 constexpr frequency& operator%=(const rep& r)
343 requires(!treat_as_inexact_v<rep>)
344 {
345 _r %= r;
346 return *this;
347 }
348
349 constexpr frequency& operator%=(const frequency& f)
350 requires(!treat_as_inexact_v<rep>)
351 {
352 _r %= f.count();
353 return *this;
354 }
355
357 static constexpr frequency zero() noexcept { return frequency(frequency_values<rep>::zero()); }
358
360 static constexpr frequency min() noexcept { return frequency(frequency_values<rep>::min()); }
361
363 static constexpr frequency max() noexcept { return frequency(frequency_values<rep>::max()); }
364
396 template<duration_like Duration>
397 constexpr Duration period() const {
398 // Period ratio is the inverse of frequency precision
399 // For frequency<Rep, ratio<N,D>>, the period of 1 tick is ratio<D,N> seconds
400 using period_ratio = std::ratio_divide<std::ratio<1>, precision>;
401 using duration_period = typename Duration::period;
402 using cf = std::ratio_divide<period_ratio, duration_period>;
403 using duration_rep = typename Duration::rep;
404
405 if (_r == rep(0)) {
406 return Duration::max();
407 }
408
409 // Integer-only path when both types are integral
410 if constexpr (std::is_integral_v<rep> && std::is_integral_v<duration_rep>) {
411 using wider_t = detail::_conversion_integer<rep, duration_rep>;
412
413 wider_t count = static_cast<wider_t>(_r);
414
415 if constexpr (cf::den == 1 && cf::num == 1) {
416 // period_ticks = 1 / freq_count (in the target duration units)
417 return Duration(static_cast<duration_rep>(1 / count));
418 } else if constexpr (cf::den == 1) {
419 // period_ticks = num / freq_count
420 // Use GCD: g = gcd(num, count), then (num/g) / (count/g)
421 wider_t num = static_cast<wider_t>(cf::num);
422 wider_t g = _runtime_gcd(num, count);
423 wider_t reduced_num = num / g;
424 wider_t reduced_count = count / g;
425 return Duration(static_cast<duration_rep>(reduced_num / reduced_count));
426 } else if constexpr (cf::num == 1) {
427 // period_ticks = 1 / (freq_count * den)
428 wider_t denom = count * static_cast<wider_t>(cf::den);
429 return Duration(static_cast<duration_rep>(1 / denom));
430 } else {
431 // period_ticks = num / (freq_count * den)
432 // Use GCD: g = gcd(num, count), then (num/g) / ((count/g) * den)
433 wider_t num = static_cast<wider_t>(cf::num);
434 wider_t g = _runtime_gcd(num, count);
435 wider_t reduced_num = num / g;
436 wider_t reduced_count = count / g;
437 wider_t denom = reduced_count * static_cast<wider_t>(cf::den);
438 return Duration(static_cast<duration_rep>(reduced_num / denom));
439 }
440 } else {
441 // Floating-point path
442 using cr = std::common_type_t<duration_rep, double>;
443
444 if constexpr (cf::den == 1 && cf::num == 1) {
445 return Duration(static_cast<duration_rep>(1.0 / static_cast<double>(_r)));
446 } else if constexpr (cf::den == 1) {
447 return Duration(static_cast<duration_rep>(static_cast<cr>(cf::num) / static_cast<double>(_r)));
448 } else if constexpr (cf::num == 1) {
449 return Duration(static_cast<duration_rep>(1.0 / (static_cast<double>(_r) * static_cast<cr>(cf::den))));
450 } else {
451 return Duration(
452 static_cast<duration_rep>(
453 static_cast<cr>(cf::num) / (static_cast<double>(_r) * static_cast<cr>(cf::den))
454 )
455 );
456 }
457 }
458 }
459
472 constexpr frequency harmonic(unsigned int n) const { return *this * n; }
473
486 constexpr frequency subharmonic(unsigned int n) const {
487 assert(n > 0 && "subharmonic divisor must be positive");
488 return *this / n;
489 }
490
514 template<typename T = double>
515 frequency octave_shift(T octaves) const {
516 double multiplier = std::pow(2.0, static_cast<double>(octaves));
517 if constexpr (std::is_integral_v<rep>) {
518 return frequency(static_cast<rep>(std::round(static_cast<double>(_r) * multiplier)));
519 } else {
520 return frequency(static_cast<rep>(static_cast<double>(_r) * multiplier));
521 }
522 }
523
548 template<typename T = double>
549 frequency semitone_shift(T semitones) const {
550 double multiplier = std::pow(2.0, static_cast<double>(semitones) / 12.0);
551 if constexpr (std::is_integral_v<rep>) {
552 return frequency(static_cast<rep>(std::round(static_cast<double>(_r) * multiplier)));
553 } else {
554 return frequency(static_cast<rep>(static_cast<double>(_r) * multiplier));
555 }
556 }
557
569 template<typename T = double>
570 T octaves_from(const frequency& other) const {
571 return static_cast<T>(std::log2(static_cast<double>(_r) / static_cast<double>(other._r)));
572 }
573
585 template<typename T = double>
586 T semitones_from(const frequency& other) const {
587 return static_cast<T>(12.0 * std::log2(static_cast<double>(_r) / static_cast<double>(other._r)));
588 }
589
608 template<distance_like Distance, duration_like Duration>
609 constexpr Distance wavelength(const Duration& time_per_unit_distance) const {
610 // Get the period of this frequency
611 using period_rep = double;
612 using period_ratio = std::ratio_divide<std::ratio<1>, precision>;
613
614 if (_r == rep(0)) {
615 return Distance::max();
616 }
617
618 // Calculate period in our internal representation (1/frequency)
619 period_rep wave_period = 1.0 / static_cast<double>(_r);
620
621 // Convert to common time representation
622 using duration_period = typename Duration::period;
623 using time_cf = std::ratio_divide<period_ratio, duration_period>;
624 using time_cr = std::common_type_t<period_rep, typename Duration::rep, double>;
625
626 // Calculate period in time_per_unit_distance units
627 time_cr period_in_duration_units;
628 if constexpr (time_cf::den == 1 && time_cf::num == 1) {
629 period_in_duration_units = static_cast<time_cr>(wave_period);
630 } else if constexpr (time_cf::den == 1) {
631 period_in_duration_units = static_cast<time_cr>(wave_period) * static_cast<time_cr>(time_cf::num);
632 } else if constexpr (time_cf::num == 1) {
633 period_in_duration_units = static_cast<time_cr>(wave_period) / static_cast<time_cr>(time_cf::den);
634 } else {
635 period_in_duration_units = static_cast<time_cr>(wave_period) * static_cast<time_cr>(time_cf::num) /
636 static_cast<time_cr>(time_cf::den);
637 }
638
639 // Wavelength = period / time_per_unit_distance
640 double wavelength_count = period_in_duration_units / static_cast<double>(time_per_unit_distance.count());
641
642 return Distance(static_cast<typename Distance::rep>(wavelength_count));
643 }
644
666 template<distance_like Distance>
667 constexpr Distance wavelength(double velocity = 299792458.0) const {
668 if (_r == rep(0)) {
669 return Distance::max();
670 }
671
672 // Convert frequency to Hz
673 double freq_hz = detail::_ratio_value<precision>(_r);
674
675 // Calculate wavelength in meters: wavelength = velocity / frequency
676 double wavelength_meters = velocity / freq_hz;
677
678 // Convert from meters to the target distance type's units
679 // Distance::period represents the ratio of the distance unit to meters
680 using distance_period = typename Distance::period;
681 double wavelength_in_units =
682 wavelength_meters * static_cast<double>(distance_period::den) / static_cast<double>(distance_period::num);
683
684 return Distance(static_cast<typename Distance::rep>(wavelength_in_units));
685 }
686
687private:
688 rep _r{};
689};
690
704template<typename ToFreq, typename Rep, typename Precision>
705 requires detail::_quantity_cast_requirements<frequency<Rep, Precision>, ToFreq>
706constexpr ToFreq frequency_cast(const frequency<Rep, Precision>& f) {
707 return detail::_quantity_cast<ToFreq>(f);
708}
709
731template<typename ToFreq, typename Rep, typename Precision>
732constexpr ToFreq floor(const frequency<Rep, Precision>& f) {
733 return detail::_floor_cast(frequency_cast<ToFreq>(f), f);
734}
735
757template<typename ToFreq, typename Rep, typename Precision>
758constexpr ToFreq ceil(const frequency<Rep, Precision>& f) {
759 return detail::_ceil_cast(frequency_cast<ToFreq>(f), f);
760}
761
779template<typename ToFreq, typename Rep, typename Precision>
780constexpr ToFreq trunc(const frequency<Rep, Precision>& f) {
781 return frequency_cast<ToFreq>(f);
782}
783
808template<typename ToFreq, typename Rep, typename Precision>
809constexpr ToFreq round(const frequency<Rep, Precision>& f) {
810 return detail::_round_cast<ToFreq>(f, [&f] { return floor<ToFreq>(f); });
811}
812
848template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
850 -> std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>> {
851 using cf = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
852 return abs(cf(f1) - cf(f2));
853}
854
870template<typename Rep, typename Precision>
872 return detail::_abs(f);
873}
874
876template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
878 -> std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>> {
879 using cf = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
880 return cf(cf(lhs).count() + cf(rhs).count());
881}
882
884template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
886 -> std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>> {
887 using cf = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
888 return cf(cf(lhs).count() - cf(rhs).count());
889}
890
892template<typename Rep1, typename Precision, typename Rep2>
893 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
894constexpr auto operator*(const frequency<Rep1, Precision>& f, const Rep2& r)
896 using cf = frequency<std::common_type_t<Rep1, Rep2>, Precision>;
897 return cf(cf(f).count() * r);
898}
899
901template<typename Rep1, typename Rep2, typename Precision>
902 requires not_frequency<Rep1> && std::convertible_to<const Rep1&, std::common_type_t<Rep1, Rep2>>
903constexpr auto operator*(const Rep1& r, const frequency<Rep2, Precision>& f)
905 return f * r;
906}
907
909template<typename Rep1, typename Precision, typename Rep2>
910 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
911constexpr auto operator/(const frequency<Rep1, Precision>& f, const Rep2& s)
913 using cf = frequency<std::common_type_t<Rep1, Rep2>, Precision>;
914 return cf(cf(f).count() / s);
915}
916
918template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
920 -> std::common_type_t<Rep1, Rep2> {
921 using cf = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
922 return cf(lhs).count() / cf(rhs).count();
923}
924
926template<typename Rep1, typename Precision, typename Rep2>
927 requires not_frequency<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>> &&
928 (!treat_as_inexact_v<Rep1> && !treat_as_inexact_v<Rep2>)
929constexpr auto operator%(const frequency<Rep1, Precision>& f, const Rep2& s)
931 using cf = frequency<std::common_type_t<Rep1, Rep2>, Precision>;
932 return cf(cf(f).count() % s);
933}
934
936template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
937 requires(!treat_as_inexact_v<Rep1> && !treat_as_inexact_v<Rep2>)
938constexpr auto operator%(const frequency<Rep1, Precision1>& lhs, const frequency<Rep2, Precision2>& rhs)
939 -> std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>> {
940 using cf = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
941 return cf(cf(lhs).count() % cf(rhs).count());
942}
943
944template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
946 using ct = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
947 return ct(lhs).count() == ct(rhs).count();
948}
949
950template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
951 requires std::three_way_comparable<std::common_type_t<Rep1, Rep2>>
953 using ct = std::common_type_t<frequency<Rep1, Precision1>, frequency<Rep2, Precision2>>;
954 return ct(lhs).count() <=> ct(rhs).count();
955}
956
994
// end of FrequencyTypes group
996
998// SI prefix for a hertz-per-count ratio; nullptr when unmapped.
999template<typename Ratio>
1000struct _si_prefix {
1001 static constexpr const char* value = nullptr;
1002};
1003template<>
1004struct _si_prefix<std::femto> {
1005 static constexpr const char* value = "f";
1006};
1007template<>
1008struct _si_prefix<std::pico> {
1009 static constexpr const char* value = "p";
1010};
1011template<>
1012struct _si_prefix<std::nano> {
1013 static constexpr const char* value = "n";
1014};
1015template<>
1016struct _si_prefix<std::micro> {
1017 static constexpr const char* value = "µ";
1018};
1019template<>
1020struct _si_prefix<std::milli> {
1021 static constexpr const char* value = "m";
1022};
1023template<>
1024struct _si_prefix<std::ratio<1>> {
1025 static constexpr const char* value = "";
1026};
1027template<>
1028struct _si_prefix<std::kilo> {
1029 static constexpr const char* value = "k";
1030};
1031template<>
1032struct _si_prefix<std::mega> {
1033 static constexpr const char* value = "M";
1034};
1035template<>
1036struct _si_prefix<std::giga> {
1037 static constexpr const char* value = "G";
1038};
1039template<>
1040struct _si_prefix<std::tera> {
1041 static constexpr const char* value = "T";
1042};
1043template<typename Ratio>
1044inline constexpr const char* _si_prefix_v = _si_prefix<typename Ratio::type>::value;
1045
1046// A value and SI prefix (of hertz) to display, for the base-unit and scaled
1047// forms.
1048struct _display {
1049 double value;
1050 const char* prefix;
1051};
1052
1053// Precisions that are exactly a power of ten of one hertz, whether or not
1054// they have an SI prefix (std::deci and std::deca do not).
1055template<typename Precision>
1056concept _decimal_precision = detail::_power_of_ten_precision<Precision>;
1057
1058// The frequency in hertz, as a double.
1059template<typename Rep, typename Precision>
1060_display _base_unit_display(const frequency<Rep, Precision>& f) {
1061 using p = typename frequency<Rep, Precision>::precision;
1062 return {detail::_ratio_value<p>(f.count()), _si_prefix_v<std::ratio<1>>};
1063}
1064
1065// The frequency with the largest SI prefix in which its magnitude is at least
1066// 1, else the smallest; zero (keeping its sign) and non-finite values are
1067// shown in hertz, as the base-unit form would show them. For a power-of-ten precision each candidate's
1068// value is computed from the count with a single scaling by a power of ten, so
1069// values that land exactly on a prefix (1000 Hz) are exactly 1 in it; other
1070// precisions are scaled from their value in hertz.
1071template<typename Rep, typename Precision>
1072_display _scaled_display(const frequency<Rep, Precision>& f) {
1073 using p = typename frequency<Rep, Precision>::precision;
1074 double x;
1075 int exponent;
1076 if constexpr (_decimal_precision<p>) {
1077 x = static_cast<double>(f.count());
1078 exponent = _precision_exponent<p>;
1079 } else {
1080 x = _base_unit_display(f).value;
1081 exponent = 0;
1082 }
1083 if (x == 0 || !std::isfinite(x)) {
1084 return {x, _si_prefix_v<std::ratio<1>>};
1085 }
1086 // The prefixes _si_prefix maps, femto to tera
1087 const detail::_scaled s = detail::_pick_scale(x, exponent, detail::_si_scale);
1088 return {s.value, s.label};
1089}
1090
1091#if CONFIG_QUANTA_STD_FORMAT
1092// How std::format renders a frequency of the given (reduced) precision; see
1093// detail::_unit_formatter. A precision with no SI prefix has no own unit.
1094template<typename Precision>
1095struct _format_traits {
1096 static constexpr const char* markers = "bs";
1097 static constexpr bool has_own_unit = _si_prefix_v<Precision> != nullptr;
1098
1099 // Reports "{}" for a precision with no own unit. The parameter only gives
1100 // the function a path that is a constant expression, as a constexpr
1101 // function needs; the formatter always calls it with true.
1102 static constexpr void no_own_unit_error(bool fail = true) {
1103 if (fail) {
1104 _format_error("frequency: precision has no SI prefix; use an explicit format spec");
1105 }
1106 }
1107
1108 // The own unit is written from constants; there is nothing to obtain.
1109 static constexpr bool own_unit(bool power) { return power; }
1110
1111 template<typename OutputIt>
1112 static OutputIt append_own_unit(OutputIt out, bool) {
1113 out = _format_append(out, _si_prefix_v<Precision>);
1114 return _format_append(out, "Hz");
1115 }
1116
1117 template<typename F>
1118 static _display base_display(const F& f) {
1119 return _base_unit_display(f);
1120 }
1121
1122 template<typename F>
1123 static _display scaled_display(const F& f) {
1124 return _scaled_display(f);
1125 }
1126
1127 template<typename OutputIt>
1128 static OutputIt append_label(OutputIt out, const _display& d) {
1129 out = _format_append(out, d.prefix);
1130 return _format_append(out, "Hz");
1131 }
1132};
1133#endif
1134
1150using quanta::base_unit;
1152
1170using quanta::scaled;
1171using quanta::scaled_t;
1172
1188template<typename Rep, typename Precision>
1190 using precision = typename frequency<Rep, Precision>::precision;
1191 static_assert(_si_prefix_v<precision> != nullptr, "frequency: precision has no SI prefix");
1192 return _count_string(f.count()) + _si_prefix_v<precision> + "Hz";
1193}
1194
1204template<typename Rep, typename Precision>
1206 const _display d = _base_unit_display(f);
1207 return _fixed_string(d.value) + d.prefix + "Hz";
1208}
1209
1220template<typename Rep, typename Precision>
1222 const _display d = _scaled_display(f);
1223 return _fixed_string(d.value) + d.prefix + "Hz";
1224}
1225
1226} // namespace quanta::frequency
1227
1228namespace std {
1229
1230template<typename Rep1, typename Precision1, typename Rep2, typename Precision2>
1231struct common_type<quanta::frequency::frequency<Rep1, Precision1>, quanta::frequency::frequency<Rep2, Precision2>> {
1232private:
1233 using common_precision = quanta::detail::_common_precision<Precision1, Precision2>;
1234
1235public:
1237};
1238
1239#if CONFIG_QUANTA_STD_FORMAT
1240// "{}" prints the exact stored count with a precision-qualified unit
1241// (kilohertz(433) -> "433kHz"; floating-point counts in the shortest fixed
1242// notation that round-trips, as to_string does: frequency<double>(1e6) ->
1243// "1000000Hz", never "1e+06Hz"). A non-empty spec controls only how the number
1244// is rendered (as double), never the unit, so both forms use the same unit:
1245// std::format("{:.1f}", millihertz(1500)) == "1500.0mHz".
1246//
1247// The unit changes only when the spec opts in with a single leading marker
1248// letter; the rest of the spec is then handled as above:
1249// - 'b' (base unit, see quanta::base_unit) shows the value in hertz:
1250// std::format("{:b.1f}", millihertz(1500)) == "1.5Hz".
1251// - 's' (scaled, see quanta::scaled) shows the value with the SI prefix suited
1252// to its magnitude: std::format("{:s}", megahertz(2400)) == "2.4GHz".
1253// With 'b' or 's' and nothing after the marker, the value is rendered in the
1254// shortest fixed notation that round-trips (never scientific), exactly as by
1255// to_string(f, quanta::base_unit) or to_string(f, quanta::scaled):
1256// std::format("{:b}", millihertz(24)) == "0.024Hz".
1257//
1258// A spec starting with 'b' or 's' is always a marker, so these letters cannot
1259// be the fill character of a spec that starts with a fill ("{:s>10.1f}" is the
1260// marker followed by ">10.1f").
1261//
1262// A precision with no SI prefix (e.g. std::deci) has no unit of its own: "{}"
1263// is a format error (a compile error for a constant format string), and a spec
1264// without a marker renders the value in hertz, as 'b' does:
1265// std::format("{:.1f}", frequency<int64_t, std::deci>(225)) == "22.5Hz".
1266template<typename Rep, typename Precision>
1267struct formatter<quanta::frequency::frequency<Rep, Precision>>
1268 : private quanta::detail::_unit_formatter<
1269 quanta::frequency::_format_traits<typename quanta::frequency::frequency<Rep, Precision>::precision>> {
1270 using quanta::detail::_unit_formatter<
1271 quanta::frequency::_format_traits<typename quanta::frequency::frequency<Rep, Precision>::precision>>::parse;
1272
1273 template<typename FormatContext>
1274 auto format(const quanta::frequency::frequency<Rep, Precision>& f, FormatContext& ctx) const {
1275 return this->format_quantity(f, ctx);
1276 }
1277};
1278#endif
1279
1280} // namespace std
1281
1285namespace quanta {
1286inline namespace literals {
1287inline namespace frequency_literals {
1289template<char... Digits>
1291 return detail::check_overflow<quanta::frequency::millihertz, Digits...>();
1292}
1293
1295template<char... Digits>
1296constexpr quanta::frequency::hertz operator""_Hz() {
1297 return detail::check_overflow<quanta::frequency::hertz, Digits...>();
1298}
1299
1301template<char... Digits>
1302constexpr quanta::frequency::kilohertz operator""_kHz() {
1303 return detail::check_overflow<quanta::frequency::kilohertz, Digits...>();
1304}
1305
1307template<char... Digits>
1308constexpr quanta::frequency::megahertz operator""_MHz() {
1309 return detail::check_overflow<quanta::frequency::megahertz, Digits...>();
1310}
1311
1313template<char... Digits>
1314constexpr quanta::frequency::gigahertz operator""_GHz() {
1315 return detail::check_overflow<quanta::frequency::gigahertz, Digits...>();
1316}
1317
1319template<char... Digits>
1320constexpr quanta::frequency::terahertz operator""_THz() {
1321 return detail::check_overflow<quanta::frequency::terahertz, Digits...>();
1322}
1323
1324} // namespace frequency_literals
1325} // namespace literals
1326
1327namespace frequency {
1328using namespace literals::frequency_literals;
1329} // namespace frequency
1330} // namespace quanta
A frequency value with a representation and precision.
constexpr Distance wavelength(const Duration &time_per_unit_distance) const
Calculates the wavelength for this frequency.
constexpr frequency & operator++()
constexpr frequency operator--(int)
typename Precision::type precision
The precision as a std::ratio.
constexpr frequency< typename std::common_type< rep >::type, precision > operator-() const
constexpr rep count() const
Returns the tick count.
constexpr frequency operator++(int)
static constexpr frequency min() noexcept
Returns the minimum representable frequency.
Rep rep
The representation type.
constexpr frequency(const frequency< Rep2, Precision2 > &f)
Explicit constructor for lossy conversions.
static constexpr frequency max() noexcept
Returns the maximum representable frequency.
constexpr frequency & operator*=(const rep &r)
constexpr frequency harmonic(unsigned int n) const
Returns the nth harmonic of this frequency.
frequency octave_shift(T octaves) const
Returns this frequency shifted by a number of octaves.
constexpr frequency()=default
Constructs a zero frequency.
constexpr frequency< typename std::common_type< rep >::type, precision > operator+() const
constexpr Duration period() const
Returns the period of this frequency as a duration.
constexpr frequency(const frequency< Rep2, Precision2 > &f)
Constructs from another frequency with different representation or precision.
T semitones_from(const frequency &other) const
Calculates the interval in semitones between this frequency and another.
constexpr frequency & operator+=(const frequency &f)
frequency semitone_shift(T semitones) const
Returns this frequency shifted by a number of semitones.
constexpr frequency & operator--()
frequency & operator=(const frequency &)=default
constexpr frequency subharmonic(unsigned int n) const
Returns the nth subharmonic of this frequency.
constexpr Distance wavelength(double velocity=299792458.0) const
Calculates the wavelength for this frequency given a propagation velocity.
constexpr frequency & operator%=(const frequency &f)
static constexpr frequency zero() noexcept
Returns a zero frequency.
T octaves_from(const frequency &other) const
Calculates the interval in octaves between this frequency and another.
constexpr frequency & operator/=(const rep &r)
constexpr frequency(const Rep2 &r)
Constructs from a tick count.
constexpr frequency & operator%=(const rep &r)
frequency(const frequency &)=default
constexpr frequency & operator-=(const frequency &f)
Configuration, traits and internals shared by every quanta domain.
quantity< watt_unit, Rep, Precision > power
Power quantity with configurable representation and precision.
Definition electro.hpp:900
frequency< int64_t > hertz
Frequency with 1 Hz precision.
frequency< int64_t, std::kilo > kilohertz
Frequency with 1000 Hz (kilohertz) precision.
frequency< int64_t, std::mega > megahertz
Frequency with 1,000,000 Hz (megahertz) precision.
frequency< int64_t, std::milli > millihertz
Frequency with 0.001 Hz (millihertz) precision.
frequency< int64_t, std::giga > gigahertz
Frequency with 1,000,000,000 Hz (gigahertz) precision.
frequency< int64_t, std::tera > terahertz
Frequency with 1,000,000,000,000 Hz (terahertz) precision.
Frequency types and utilities.
constexpr ToFreq trunc(const frequency< Rep, Precision > &f)
Converts a frequency to the target type, rounding toward zero.
constexpr bool is_frequency_v
constexpr auto operator+(const frequency< Rep1, Precision1 > &lhs, const frequency< Rep2, Precision2 > &rhs) -> std::common_type_t< frequency< Rep1, Precision1 >, frequency< Rep2, Precision2 > >
Returns the sum of two frequencies.
constexpr auto operator-(const frequency< Rep1, Precision1 > &lhs, const frequency< Rep2, Precision2 > &rhs) -> std::common_type_t< frequency< Rep1, Precision1 >, frequency< Rep2, Precision2 > >
Returns the difference of two frequencies.
constexpr ToFreq round(const frequency< Rep, Precision > &f)
Converts a frequency to the target type, rounding to nearest (ties to even).
constexpr auto operator/(const frequency< Rep1, Precision > &f, const Rep2 &s) -> frequency< std::common_type_t< Rep1, Rep2 >, Precision >
Divides a frequency by a scalar.
constexpr ToFreq floor(const frequency< Rep, Precision > &f)
Converts a frequency to the target type, rounding toward negative infinity.
constexpr auto operator<=>(const frequency< Rep1, Precision1 > &lhs, const frequency< Rep2, Precision2 > &rhs)
constexpr bool operator==(const frequency< Rep1, Precision1 > &lhs, const frequency< Rep2, Precision2 > &rhs)
constexpr ToFreq ceil(const frequency< Rep, Precision > &f)
Converts a frequency to the target type, rounding toward positive infinity.
constexpr frequency< Rep, Precision > abs(const frequency< Rep, Precision > &f)
Returns the absolute value of a frequency.
std::string to_string(const frequency< Rep, Precision > &f)
Converts a frequency to a string with its precision-qualified unit.
constexpr auto beat(const frequency< Rep1, Precision1 > &f1, const frequency< Rep2, Precision2 > &f2) -> std::common_type_t< frequency< Rep1, Precision1 >, frequency< Rep2, Precision2 > >
Calculates the beat frequency between two frequencies.
constexpr auto operator*(const frequency< Rep1, Precision > &f, const Rep2 &r) -> frequency< std::common_type_t< Rep1, Rep2 >, Precision >
Multiplies a frequency by a scalar.
constexpr ToFreq frequency_cast(const frequency< Rep, Precision > &f)
Converts a frequency to a different precision or representation.
Type-safe units modeled after std::chrono.
Definition core.hpp:90
constexpr scaled_t scaled
Selects the scaled form in to_string().
Definition core.hpp:161
constexpr base_unit_t base_unit
Selects the base-unit form in to_string().
Definition core.hpp:140
constexpr bool treat_as_inexact_v
Definition core.hpp:119
Tag type selecting the base-unit form, see base_unit.
Definition core.hpp:124
Trait to detect frequency specializations.
Tag type selecting the scaled form, see scaled.
Definition core.hpp:145
Trait to indicate floating-point-like behavior.
Definition core.hpp:116