@@ -1099,28 +1099,75 @@ using terahertz = frequency<int64_t, std::tera>;
10991099
11001100/* * @} */ // end of FrequencyTypes group
11011101
1102- inline std::string to_string (millihertz f) {
1103- return std::to_string (f.count ()) + " mHz" ;
1104- }
1105-
1106- inline std::string to_string (hertz f) {
1107- return std::to_string (f.count ()) + " Hz" ;
1108- }
1109-
1110- inline std::string to_string (kilohertz f) {
1111- return std::to_string (f.count ()) + " kHz" ;
1112- }
1113-
1114- inline std::string to_string (megahertz f) {
1115- return std::to_string (f.count ()) + " MHz" ;
1116- }
1117-
1118- inline std::string to_string (gigahertz f) {
1119- return std::to_string (f.count ()) + " GHz" ;
1102+ /* * @cond INTERNAL */
1103+ // SI prefix for a hertz-per-count ratio; nullptr when unmapped.
1104+ template <typename Ratio>
1105+ struct _si_prefix {
1106+ static constexpr const char * value = nullptr ;
1107+ };
1108+ template <>
1109+ struct _si_prefix <std::femto> {
1110+ static constexpr const char * value = " f" ;
1111+ };
1112+ template <>
1113+ struct _si_prefix <std::pico> {
1114+ static constexpr const char * value = " p" ;
1115+ };
1116+ template <>
1117+ struct _si_prefix <std::nano> {
1118+ static constexpr const char * value = " n" ;
1119+ };
1120+ template <>
1121+ struct _si_prefix <std::micro> {
1122+ static constexpr const char * value = " µ" ;
1123+ };
1124+ template <>
1125+ struct _si_prefix <std::milli> {
1126+ static constexpr const char * value = " m" ;
1127+ };
1128+ template <>
1129+ struct _si_prefix <std::ratio<1 >> {
1130+ static constexpr const char * value = " " ;
1131+ };
1132+ template <>
1133+ struct _si_prefix <std::kilo> {
1134+ static constexpr const char * value = " k" ;
1135+ };
1136+ template <>
1137+ struct _si_prefix <std::mega> {
1138+ static constexpr const char * value = " M" ;
1139+ };
1140+ template <>
1141+ struct _si_prefix <std::giga> {
1142+ static constexpr const char * value = " G" ;
1143+ };
1144+ template <>
1145+ struct _si_prefix <std::tera> {
1146+ static constexpr const char * value = " T" ;
1147+ };
1148+ template <typename Ratio>
1149+ inline constexpr const char * _si_prefix_v = _si_prefix<typename Ratio::type>::value;
1150+
1151+ // Appends a NUL-terminated string to a format output iterator.
1152+ template <typename OutputIt>
1153+ constexpr OutputIt _format_append (OutputIt out, const char * s) {
1154+ for (; *s != ' \0 ' ; ++s) {
1155+ *out++ = *s;
1156+ }
1157+ return out;
11201158}
1159+ /* * @endcond */
11211160
1122- inline std::string to_string (terahertz f) {
1123- return std::to_string (f.count ()) + " THz" ;
1161+ /* *
1162+ * @brief Renders a frequency as its exact count with a precision-qualified
1163+ * unit, e.g. `to_string(kilohertz(433)) == "433kHz"`.
1164+ */
1165+ template <typename Rep, typename Precision>
1166+ requires (!treat_as_inexact_v<Rep>)
1167+ std::string to_string (const frequency<Rep, Precision>& f) {
1168+ using precision = typename frequency<Rep, Precision>::precision;
1169+ static_assert (_si_prefix_v<precision> != nullptr , " frequency: precision has no SI prefix" );
1170+ return std::to_string (f.count ()) + _si_prefix_v<precision> + " Hz" ;
11241171}
11251172
11261173} // namespace freq
@@ -1139,46 +1186,41 @@ struct common_type<freq::frequency<Rep1, Precision1>, freq::frequency<Rep2, Prec
11391186};
11401187
11411188#if CONFIG_FREQUENCY_STD_FORMAT
1142- template <>
1143- struct formatter <freq::millihertz> {
1144- constexpr auto parse (format_parse_context& ctx) { return ctx.begin (); }
1145-
1146- auto format (freq::millihertz f, format_context& ctx) const { return std::format_to (ctx.out (), " {}mHz" , f.count ()); }
1147- };
1148-
1149- template <>
1150- struct formatter <freq::hertz> {
1151- constexpr auto parse (format_parse_context& ctx) { return ctx.begin (); }
1152-
1153- auto format (freq::hertz f, format_context& ctx) const { return std::format_to (ctx.out (), " {}Hz" , f.count ()); }
1154- };
1155-
1156- template <>
1157- struct formatter <freq::kilohertz> {
1158- constexpr auto parse (format_parse_context& ctx) { return ctx.begin (); }
1159-
1160- auto format (freq::kilohertz f, format_context& ctx) const { return std::format_to (ctx.out (), " {}kHz" , f.count ()); }
1161- };
1162-
1163- template <>
1164- struct formatter <freq::megahertz> {
1165- constexpr auto parse (format_parse_context& ctx) { return ctx.begin (); }
1166-
1167- auto format (freq::megahertz f, format_context& ctx) const { return std::format_to (ctx.out (), " {}MHz" , f.count ()); }
1168- };
1169-
1170- template <>
1171- struct formatter <freq::gigahertz> {
1172- constexpr auto parse (format_parse_context& ctx) { return ctx.begin (); }
1173-
1174- auto format (freq::gigahertz f, format_context& ctx) const { return std::format_to (ctx.out (), " {}GHz" , f.count ()); }
1175- };
1189+ // "{}" prints the exact stored count with a precision-qualified unit
1190+ // (kilohertz(433) -> "433kHz"). A non-empty spec is applied to the value in
1191+ // hertz (as double): std::format("{:.1f}", millihertz(1500)) == "1.5Hz".
1192+ template <typename Rep, typename Precision>
1193+ struct formatter <freq::frequency<Rep, Precision>> {
1194+ private:
1195+ using _precision = typename freq::frequency<Rep, Precision>::precision;
1196+ static constexpr const char * _prefix = freq::_si_prefix_v<_precision>;
1197+ std::formatter<double > _num;
1198+ bool _has_spec = false ;
11761199
1177- template <>
1178- struct formatter <freq::terahertz> {
1179- constexpr auto parse (format_parse_context& ctx) { return ctx.begin (); }
1200+ public:
1201+ constexpr auto parse (format_parse_context& ctx) {
1202+ auto it = ctx.begin ();
1203+ if (it == ctx.end () || *it == ' }' ) {
1204+ if (_prefix == nullptr ) {
1205+ throw format_error (" frequency: precision has no SI prefix; use an explicit format spec" );
1206+ }
1207+ return it;
1208+ }
1209+ _has_spec = true ;
1210+ return _num.parse (ctx);
1211+ }
11801212
1181- auto format (freq::terahertz f, format_context& ctx) const { return std::format_to (ctx.out (), " {}THz" , f.count ()); }
1213+ template <typename FormatContext>
1214+ auto format (const freq::frequency<Rep, Precision>& f, FormatContext& ctx) const {
1215+ if (_has_spec) {
1216+ double hz = static_cast <double >(f.count ()) * _precision::num / _precision::den;
1217+ auto out = _num.format (hz, ctx);
1218+ return freq::_format_append (out, " Hz" );
1219+ }
1220+ auto out = std::format_to (ctx.out (), " {}" , f.count ());
1221+ out = freq::_format_append (out, _prefix);
1222+ return freq::_format_append (out, " Hz" );
1223+ }
11821224};
11831225#endif
11841226
0 commit comments