16#ifndef CONFIG_THERMO_STD_FORMAT
17#if __has_include(<format>) && defined(__cpp_lib_format)
18#define CONFIG_THERMO_STD_FORMAT 1
20#define CONFIG_THERMO_STD_FORMAT 0
24#if CONFIG_THERMO_STD_FORMAT
29static_assert(__cplusplus >= 202302L,
"thermo requires C++23 or later");
41template<
typename Rep,
typename Precision = std::ratio<1>>
51template<
typename Rep,
typename Precision>
55template<
typename T,
template<
typename...>
class Template>
56struct _is_specialization_of : std::false_type {};
58template<
template<
typename...>
class Template,
typename... Args>
59struct _is_specialization_of<Template<Args...>, Template> : std::true_type {};
61template<
typename T,
template<
typename...>
class Template>
62inline constexpr bool _is_specialization_of_v = _is_specialization_of<T, Template>::value;
65concept not_delta = !_is_specialization_of_v<Rep, delta>;
70 static constexpr Rep zero() noexcept {
return Rep(0); }
73 static constexpr Rep max() noexcept {
return std::numeric_limits<Rep>::max(); }
76 static constexpr Rep min() noexcept {
return std::numeric_limits<Rep>::lowest(); }
80struct _is_ratio : std::false_type {};
82template<std::
intmax_t Num, std::
intmax_t Denom>
83struct _is_ratio<
std::ratio<Num, Denom>> : std::true_type {};
86struct treat_as_inexact : std::bool_constant<std::floating_point<T>> {};
89inline constexpr bool treat_as_inexact_v = treat_as_inexact<T>::value;
91consteval intmax_t _gcd(intmax_t m, intmax_t n)
noexcept {
100template<
typename R1,
typename R2>
101inline constexpr intmax_t _safe_ratio_divide_den = [] {
102 constexpr intmax_t g1 = _gcd(R1::num, R2::num);
103 constexpr intmax_t g2 = _gcd(R1::den, R2::den);
104 return (R1::den / g2) * (R2::num / g1);
107template<
typename From,
typename To>
108concept _harmonic_precision = _safe_ratio_divide_den<From, To> == 1;
121template<
typename Rep,
typename Precision>
124 static_assert(_is_ratio<Precision>::value,
"precision must be a specialization of std::ratio");
125 static_assert(Precision::num > 0,
"precision must be positive");
146 template<
typename Rep2>
147 requires std::convertible_to<const Rep2&, rep> && (treat_as_inexact_v<rep> || !treat_as_inexact_v<Rep2>)
148 constexpr explicit delta(
const Rep2& r)
149 : _r(
static_cast<rep>(r)) {}
161 template<
typename Rep2,
typename Precision2>
162 requires std::convertible_to<const Rep2&, rep> &&
163 (treat_as_inexact_v<rep> || (_harmonic_precision<Precision2, precision> && !treat_as_inexact_v<Rep2>))
216 requires(!treat_as_inexact_v<rep>)
223 requires(!treat_as_inexact_v<rep>)
230 static constexpr delta zero() noexcept {
return delta(delta_values<rep>::zero()); }
233 static constexpr delta min() noexcept {
return delta(delta_values<rep>::min()); }
236 static constexpr delta max() noexcept {
return delta(delta_values<rep>::max()); }
252template<
typename ToDelta,
typename Rep,
typename Precision>
254 if constexpr (std::is_same_v<ToDelta, delta<Rep, Precision>>) {
257 using to_rep =
typename ToDelta::rep;
258 using to_precision =
typename ToDelta::precision;
259 using cf = std::ratio_divide<Precision, to_precision>;
260 using cr = std::common_type_t<to_rep, Rep, intmax_t>;
262 if constexpr (cf::den == 1 && cf::num == 1) {
263 return ToDelta(
static_cast<to_rep
>(d.
count()));
264 }
else if constexpr (cf::den == 1) {
265 return ToDelta(
static_cast<to_rep
>(
static_cast<cr
>(d.
count()) *
static_cast<cr
>(cf::num)));
266 }
else if constexpr (cf::num == 1) {
267 return ToDelta(
static_cast<to_rep
>(
static_cast<cr
>(d.
count()) /
static_cast<cr
>(cf::den)));
270 static_cast<to_rep
>(
static_cast<cr
>(d.
count()) *
static_cast<cr
>(cf::num) /
static_cast<cr
>(cf::den))
288template<
typename ToDelta,
typename Rep,
typename Precision>
309template<
typename ToDelta,
typename Rep,
typename Precision>
330template<
typename ToDelta,
typename Rep,
typename Precision>
348template<
typename ToDelta,
typename Rep,
typename Precision>
371 if (
d.count() >= 0) {
382template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
390template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
398template<
typename Rep1,
typename Precision,
typename Rep2>
399 requires not_delta<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
403 return cd(
cd(
d).count() *
r);
407template<
typename Rep1,
typename Rep2,
typename Precision>
408 requires not_delta<Rep1> && std::convertible_to<const Rep1&, std::common_type_t<Rep1, Rep2>>
415template<
typename Rep1,
typename Precision,
typename Rep2>
416 requires not_delta<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
420 return cd(
cd(
d).count() /
s);
424template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
426 -> std::common_type_t<Rep1, Rep2> {
432template<
typename Rep1,
typename Precision,
typename Rep2>
433 requires not_delta<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>> &&
438 return cd(
cd(
d).count() %
s);
442template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
450template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
456template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
457 requires std::three_way_comparable<std::common_type_t<Rep1, Rep2>>
484template<
typename Ratio>
486 static constexpr const char*
value =
nullptr;
490 static constexpr const char*
value =
"";
494 static constexpr const char*
value =
"d";
498 static constexpr const char*
value =
"m";
500template<
typename Ratio>
501inline constexpr const char*
_si_prefix_v = _si_prefix<typename Ratio::type>::value;
506template<
typename Precision>
509 using degree = std::ratio<1>;
510 static constexpr const char*
suffix =
"°C";
513 using degree = std::ratio<5, 9>;
514 static constexpr const char*
suffix =
"°F";
530template<
typename OutputIt>
532 for (; *
s !=
'\0'; ++
s) {
538#if CONFIG_THERMO_STD_FORMAT
548#if defined(__cpp_exceptions) && __cpp_exceptions
549 throw std::format_error(
what);
562template<
typename Rep,
typename Precision>
566 using dpc = std::ratio_divide<typename delta<Rep, Precision>::precision,
typename unit::degree>;
567 static_assert(
_si_prefix_v<dpc> !=
nullptr,
"thermo: precision has no SI prefix");
582 using offset = std::ratio<27315, 100>;
583 using degree = std::ratio<1>;
584 static constexpr const char*
suffix =
"°C";
593 using offset = std::ratio<0>;
594 using degree = std::ratio<1>;
595 static constexpr const char*
suffix =
"K";
604 using offset = std::ratio<45967, 180>;
605 using degree = std::ratio<5, 9>;
606 static constexpr const char*
suffix =
"°F";
617template<
typename Scale>
618inline constexpr bool _is_relative_scale = std::ratio_not_equal_v<typename Scale::offset, std::ratio<0>>;
621template<
typename Scale,
typename Delta = delta<
int64_t>>
625template<
typename Scale1,
typename Delta1,
typename Scale2,
typename Delta2>
627 using from_prec =
typename Delta1::precision;
628 using to_prec =
typename Delta2::precision;
629 using from_off =
typename Scale1::offset;
630 using to_off =
typename Scale2::offset;
632 using prec_ratio = std::ratio_divide<from_prec, to_prec>;
633 using offset_diff = std::ratio_subtract<from_off, to_off>;
636 static constexpr bool value = prec_ratio::den == 1 && offset_in_target_units::den == 1;
639template<
typename Scale1,
typename Delta1,
typename Scale2,
typename Delta2>
649template<
typename Scale,
typename Delta>
666template<
typename ToTemp,
typename Scale,
typename Delta>
679template<
typename Scale,
typename Delta>
689 using rep =
typename Delta::rep;
700 template<
typename Rep2>
703 : _d(
static_cast<rep>(
r)) {}
713 template<
typename Delta2>
714 requires(!std::is_same_v<Delta, Delta2>) &&
722 template<
typename Delta2>
729 template<
typename Scale2,
typename Delta2>
730 requires(!std::is_same_v<temperature, temperature<Scale2, Delta2>>) &&
737 template<
typename Scale2,
typename Delta2>
738 requires(!std::is_same_v<temperature, temperature<Scale2, Delta2>>) &&
776 template<
typename Rep2,
typename Precision2>
782 template<
typename Rep2,
typename Precision2>
798template<
typename ToTemp,
typename Scale,
typename Delta>
800 using ToScale =
typename ToTemp::scale;
801 using ToDelta =
typename ToTemp::delta_type;
802 using to_rep =
typename ToDelta::rep;
804 if constexpr (std::is_same_v<Scale, ToScale> && std::is_same_v<Delta, ToDelta>) {
806 }
else if constexpr (std::is_same_v<Scale, ToScale>) {
809 using from_prec =
typename Delta::precision;
810 using from_off =
typename Scale::offset;
811 using to_prec =
typename ToDelta::precision;
812 using to_off =
typename ToScale::offset;
814 using offset_diff = std::ratio_subtract<from_off, to_off>;
815 using common_rep = std::common_type_t<typename Delta::rep, to_rep, intmax_t>;
819 using prec_ratio = std::ratio_divide<from_prec, to_prec>;
820 using offset_ratio = std::ratio_divide<offset_diff, to_prec>;
823 constexpr double pr =
static_cast<double>(prec_ratio::num) / prec_ratio::den;
824 constexpr double or_ =
static_cast<double>(offset_ratio::num) / offset_ratio::den;
829 static_cast<intmax_t>(prec_ratio::den) *
static_cast<intmax_t>(offset_ratio::den);
840template<
typename Scale,
typename Delta1,
typename Delta2>
843 using cd = std::common_type_t<Delta1, Delta2>;
848template<
typename Scale,
typename Delta1,
typename Delta2>
851 using cd = std::common_type_t<Delta1, Delta2>;
863template<
typename Scale,
typename Delta1,
typename Delta2>
865 -> std::common_type_t<Delta1, Delta2> {
866 using cd = std::common_type_t<Delta1, Delta2>;
867 return cd(
lhs.count() -
rhs.count());
871template<
typename Scale,
typename Delta1,
typename Rep2,
typename Precision2>
874 using result_delta = std::common_type_t<Delta1, delta<Rep2, Precision2>>;
879template<
typename Rep1,
typename Precision1,
typename Scale,
typename Delta2>
886template<
typename Scale,
typename Delta1,
typename Rep2,
typename Precision2>
889 using result_delta = std::common_type_t<Delta1, delta<Rep2, Precision2>>;
893template<
typename Scale,
typename Delta1,
typename Delta2>
895 return lhs.count() ==
rhs.count();
898template<
typename Scale,
typename Delta1,
typename Delta2>
899 requires std::three_way_comparable<std::common_type_t<typename Delta1::rep, typename Delta2::rep>>
901 return lhs.count() <=>
rhs.count();
927template<
typename Scale,
typename Rep,
typename Precision>
930 using dpc = std::ratio_divide<typename delta<Rep, Precision>::precision,
typename Scale::degree>;
931 static_assert(
_si_prefix_v<dpc> !=
nullptr,
"thermo: precision has no SI prefix");
939template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
942 using common_precision = std::ratio<
944 (Precision1::den /
thermo::_gcd(Precision1::den, Precision2::den)) * Precision2::den>;
950template<
typename Scale,
typename Delta1,
typename Delta2>
955#if CONFIG_THERMO_STD_FORMAT
960template<
typename Scale,
typename Rep,
typename Precision>
961struct formatter<
thermo::temperature<Scale, thermo::delta<Rep, Precision>>> {
964 using _dpc =
typename ratio_divide<typename _delta_t::precision, typename Scale::degree>::type;
966 std::formatter<double> _num;
967 bool _has_spec =
false;
970 constexpr auto parse(format_parse_context& ctx) {
971 auto it = ctx.begin();
972 if (it == ctx.end() || *it ==
'}') {
973 if constexpr (_prefix ==
nullptr) {
979 return _num.parse(ctx);
982 template<
typename FormatContext>
985 double degrees =
static_cast<double>(t.
count()) * _dpc::num / _dpc::den;
986 auto out = _num.format(degrees, ctx);
989 auto out = std::format_to(ctx.out(),
"{}", t.
count());
999template<
typename Rep,
typename Precision>
1000struct formatter<
thermo::delta<Rep, Precision>> {
1004 using _dpc =
typename ratio_divide<typename _delta_t::precision, typename _unit::degree>::type;
1006 std::formatter<double> _num;
1007 bool _has_spec =
false;
1010 constexpr auto parse(format_parse_context& ctx) {
1011 auto it = ctx.begin();
1012 if (it == ctx.end() || *it ==
'}') {
1013 if constexpr (_prefix ==
nullptr) {
1019 return _num.parse(ctx);
1022 template<
typename FormatContext>
1023 auto format(
const _delta_t& d, FormatContext& ctx)
const {
1025 double degrees =
static_cast<double>(d.count()) * _dpc::num / _dpc::den;
1026 auto out = _num.format(degrees, ctx);
1030 auto out = std::format_to(ctx.out(),
"{}", d.count());
1047template<
unsigned long long Value,
unsigned long long Power>
1049 static constexpr unsigned long long value = 10 * pow10<Value, Power - 1>::value;
1052template<
unsigned long long Value>
1053struct pow10<Value, 0> {
1054 static constexpr unsigned long long value = Value;
1057template<
char... Digits>
1060template<
char D,
char... Rest>
1061struct parse_int<D, Rest...> {
1062 static_assert(D >=
'0' && D <=
'9',
"invalid digit");
1063 static constexpr unsigned long long value = pow10<D -
'0',
sizeof...(Rest)>::value + parse_int<Rest...>::value;
1067struct parse_int<D> {
1068 static_assert(D >=
'0' && D <=
'9',
"invalid digit");
1069 static constexpr unsigned long long value = D -
'0';
1072template<
typename Delta,
char... Digits>
1073constexpr Delta check_overflow() {
1074 using parsed = parse_int<Digits...>;
1075 constexpr typename Delta::rep repval = parsed::value;
1077 repval >= 0 &&
static_cast<unsigned long long>(repval) == parsed::value,
1078 "literal value cannot be represented by delta type"
1080 return Delta(repval);
1087template<
char... Digits>
1089 return thermo::celsius(detail::check_overflow<thermo::delta_celsius, Digits...>());
1093template<
char... Digits>
1095 return thermo::decicelsius(detail::check_overflow<thermo::delta_decicelsius, Digits...>());
1099template<
char... Digits>
1105template<
char... Digits>
1107 return thermo::kelvin(detail::check_overflow<thermo::delta_kelvin, Digits...>());
1111template<
char... Digits>
1113 return thermo::decikelvin(detail::check_overflow<thermo::delta_decikelvin, Digits...>());
1117template<
char... Digits>
1119 return thermo::millikelvin(detail::check_overflow<thermo::delta_millikelvin, Digits...>());
1123template<
char... Digits>
1125 return thermo::fahrenheit(detail::check_overflow<thermo::delta_fahrenheit, Digits...>());
1129template<
char... Digits>
1135template<
char... Digits>
1141template<
char... Digits>
1147template<
char... Digits>
1153template<
char... Digits>
1159template<
char... Digits>
1165template<
char... Digits>
1171template<
char... Digits>
A temperature difference with a representation and precision.
constexpr delta & operator*=(const rep &r)
static constexpr delta zero() noexcept
Returns a zero-length delta.
constexpr delta & operator++()
static constexpr delta min() noexcept
Returns the minimum (most negative) representable delta.
constexpr delta()=default
Constructs a zero delta.
constexpr delta & operator/=(const rep &r)
static constexpr delta max() noexcept
Returns the maximum representable delta.
Rep rep
The representation type.
constexpr delta & operator--()
constexpr rep count() const
Returns the tick count.
constexpr delta operator--(int)
delta & operator=(const delta &)=default
constexpr delta & operator-=(const delta &d)
constexpr delta & operator%=(const delta &d)
typename Precision::type precision
The precision as a std::ratio.
delta(const delta &)=default
constexpr delta< typename std::common_type< rep >::type, precision > operator+() const
constexpr delta operator++(int)
constexpr delta(const Rep2 &r)
Constructs from a tick count.
constexpr delta & operator+=(const delta &d)
constexpr delta< typename std::common_type< rep >::type, precision > operator-() const
constexpr delta & operator%=(const rep &r)
An absolute temperature on a given scale.
typename Delta::rep rep
The representation type.
Delta delta_type
The delta type.
constexpr temperature()=default
Constructs a temperature at the scale's zero point.
constexpr temperature operator-() const
Negates the temperature (e.g.
constexpr temperature & operator++()
constexpr temperature(const temperature< Scale, Delta2 > &t)
constexpr temperature(const temperature< Scale2, Delta2 > &t)
constexpr temperature(const Rep2 &r)
Constructs from a tick count.
static constexpr temperature max() noexcept
Returns the maximum representable temperature.
constexpr temperature operator--(int)
constexpr temperature(const temperature< Scale, Delta2 > &t)
temperature(const temperature &)=default
constexpr temperature(const Delta &d)
Constructs from a delta.
static constexpr temperature min() noexcept
Returns the minimum representable temperature.
constexpr temperature operator++(int)
constexpr temperature & operator+=(const delta< Rep2, Precision2 > &d)
Scale scale
The temperature scale.
constexpr temperature & operator--()
constexpr rep count() const
Returns the tick count.
constexpr temperature & operator-=(const delta< Rep2, Precision2 > &d)
temperature & operator=(const temperature &)=default
constexpr temperature(const temperature< Scale2, Delta2 > &t)
User-defined literals for temperature types.
Temperature types and utilities.
temperature< celsius_scale > celsius
Celsius with 1 degree precision.
constexpr auto operator-(const delta< Rep1, Precision1 > &lhs, const delta< Rep2, Precision2 > &rhs) -> std::common_type_t< delta< Rep1, Precision1 >, delta< Rep2, Precision2 > >
Returns the difference of two deltas.
std::string to_string(const delta< Rep, Precision > &d)
Renders a delta as its exact count with a precision-qualified unit, e.g.
temperature< fahrenheit_scale, delta< int64_t, std::ratio< 5, 9000 > > > millifahrenheit
Fahrenheit with 0.001 degree precision.
delta< int64_t, std::milli > delta_millicelsius
Delta with 0.001 degree precision (Celsius/Kelvin).
constexpr ToDelta round(const delta< Rep, Precision > &d)
Rounds a delta to the nearest representable value in the target precision.
constexpr auto operator+(const delta< Rep1, Precision1 > &lhs, const delta< Rep2, Precision2 > &rhs) -> std::common_type_t< delta< Rep1, Precision1 >, delta< Rep2, Precision2 > >
Returns the sum of two deltas.
constexpr ToDelta delta_cast(const delta< Rep, Precision > &d)
Converts a delta to a different precision or representation.
constexpr bool operator==(const delta< Rep1, Precision1 > &lhs, const delta< Rep2, Precision2 > &rhs)
delta< int64_t, std::milli > delta_millikelvin
Delta with 0.001 degree precision (Celsius/Kelvin).
constexpr auto operator*(const delta< Rep1, Precision > &d, const Rep2 &r) -> delta< std::common_type_t< Rep1, Rep2 >, Precision >
Multiplies a delta by a scalar.
delta< int64_t, std::ratio< 5, 9000 > > delta_millifahrenheit
Delta with 0.001°F precision.
temperature< fahrenheit_scale, delta< int64_t, std::ratio< 5, 9 > > > fahrenheit
Fahrenheit with 1 degree precision.
constexpr ToDelta ceil(const delta< Rep, Precision > &d)
Rounds a delta up to the nearest representable value in the target precision.
delta< int64_t, std::ratio< 5, 9 > > delta_fahrenheit
Delta with 1°F precision.
constexpr auto difference(const temperature< Scale, Delta1 > &lhs, const temperature< Scale, Delta2 > &rhs) -> std::common_type_t< Delta1, Delta2 >
Returns the difference between two temperatures as a delta.
constexpr ToDelta floor(const delta< Rep, Precision > &d)
Rounds a delta down to the nearest representable value in the target precision.
delta< int64_t > delta_kelvin
Delta with 1 degree precision (Celsius/Kelvin).
temperature< celsius_scale, delta< int64_t, std::deci > > decicelsius
Celsius with 0.1 degree precision.
temperature< kelvin_scale, delta< int64_t, std::milli > > millikelvin
Kelvin with 0.001 degree precision.
constexpr ToTemp temperature_cast(const temperature< Scale, Delta > &t)
Converts a temperature to a different scale or precision.
constexpr auto operator<=>(const delta< Rep1, Precision1 > &lhs, const delta< Rep2, Precision2 > &rhs)
temperature< fahrenheit_scale, delta< int64_t, std::ratio< 5, 90 > > > decifahrenheit
Fahrenheit with 0.1 degree precision.
temperature< kelvin_scale, delta< int64_t, std::deci > > decikelvin
Kelvin with 0.1 degree precision.
temperature< kelvin_scale > kelvin
Kelvin with 1 degree precision.
temperature< celsius_scale, delta< int64_t, std::milli > > millicelsius
Celsius with 0.001 degree precision.
constexpr auto operator/(const delta< Rep1, Precision > &d, const Rep2 &s) -> delta< std::common_type_t< Rep1, Rep2 >, Precision >
Divides a delta by a scalar.
delta< int64_t > delta_celsius
Delta with 1 degree precision (Celsius/Kelvin).
constexpr ToDelta trunc(const delta< Rep, Precision > &d)
Rounds a delta toward zero to the nearest representable value in the target precision.
Trait to detect delta specializations.