14#ifndef CONFIG_THERMO_STD_FORMAT
15#if __has_include(<format>) && defined(__cpp_lib_format)
16#define CONFIG_THERMO_STD_FORMAT 1
18#define CONFIG_THERMO_STD_FORMAT 0
22#if CONFIG_THERMO_STD_FORMAT
27static_assert(__cplusplus >= 202302L,
"thermo requires C++23 or later");
39template<
typename Rep,
typename Precision = std::ratio<1>>
49template<
typename Rep,
typename Precision>
53template<
typename T,
template<
typename...>
class Template>
54struct _is_specialization_of : std::false_type {};
56template<
template<
typename...>
class Template,
typename... Args>
57struct _is_specialization_of<Template<Args...>, Template> : std::true_type {};
59template<
typename T,
template<
typename...>
class Template>
60inline constexpr bool _is_specialization_of_v = _is_specialization_of<T, Template>::value;
63concept not_delta = !_is_specialization_of_v<Rep, delta>;
68 static constexpr Rep zero() noexcept {
return Rep(0); }
71 static constexpr Rep max() noexcept {
return std::numeric_limits<Rep>::max(); }
74 static constexpr Rep min() noexcept {
return std::numeric_limits<Rep>::lowest(); }
78struct _is_ratio : std::false_type {};
80template<std::
intmax_t Num, std::
intmax_t Denom>
81struct _is_ratio<
std::ratio<Num, Denom>> : std::true_type {};
84struct treat_as_inexact : std::bool_constant<std::floating_point<T>> {};
87inline constexpr bool treat_as_inexact_v = treat_as_inexact<T>::value;
89consteval intmax_t _gcd(intmax_t m, intmax_t n)
noexcept {
98template<
typename R1,
typename R2>
99inline constexpr intmax_t _safe_ratio_divide_den = [] {
100 constexpr intmax_t g1 = _gcd(R1::num, R2::num);
101 constexpr intmax_t g2 = _gcd(R1::den, R2::den);
102 return (R1::den / g2) * (R2::num / g1);
105template<
typename From,
typename To>
106concept _harmonic_precision = _safe_ratio_divide_den<From, To> == 1;
119template<
typename Rep,
typename Precision>
122 static_assert(_is_ratio<Precision>::value,
"precision must be a specialization of std::ratio");
123 static_assert(Precision::num > 0,
"precision must be positive");
144 template<
typename Rep2>
145 requires std::convertible_to<const Rep2&, rep> && (treat_as_inexact_v<rep> || !treat_as_inexact_v<Rep2>)
146 constexpr explicit delta(
const Rep2& r)
147 : _r(
static_cast<rep>(r)) {}
159 template<
typename Rep2,
typename Precision2>
160 requires std::convertible_to<const Rep2&, rep> &&
161 (treat_as_inexact_v<rep> || (_harmonic_precision<Precision2, precision> && !treat_as_inexact_v<Rep2>))
214 requires(!treat_as_inexact_v<rep>)
221 requires(!treat_as_inexact_v<rep>)
228 static constexpr delta zero() noexcept {
return delta(delta_values<rep>::zero()); }
231 static constexpr delta min() noexcept {
return delta(delta_values<rep>::min()); }
234 static constexpr delta max() noexcept {
return delta(delta_values<rep>::max()); }
250template<
typename ToDelta,
typename Rep,
typename Precision>
252 if constexpr (std::is_same_v<ToDelta, delta<Rep, Precision>>) {
255 using to_rep =
typename ToDelta::rep;
256 using to_precision =
typename ToDelta::precision;
257 using cf = std::ratio_divide<Precision, to_precision>;
258 using cr = std::common_type_t<to_rep, Rep, intmax_t>;
260 if constexpr (cf::den == 1 && cf::num == 1) {
261 return ToDelta(
static_cast<to_rep
>(d.
count()));
262 }
else if constexpr (cf::den == 1) {
263 return ToDelta(
static_cast<to_rep
>(
static_cast<cr
>(d.
count()) *
static_cast<cr
>(cf::num)));
264 }
else if constexpr (cf::num == 1) {
265 return ToDelta(
static_cast<to_rep
>(
static_cast<cr
>(d.
count()) /
static_cast<cr
>(cf::den)));
268 static_cast<to_rep
>(
static_cast<cr
>(d.
count()) *
static_cast<cr
>(cf::num) /
static_cast<cr
>(cf::den))
286template<
typename ToDelta,
typename Rep,
typename Precision>
307template<
typename ToDelta,
typename Rep,
typename Precision>
328template<
typename ToDelta,
typename Rep,
typename Precision>
346template<
typename ToDelta,
typename Rep,
typename Precision>
369 if (
d.count() >= 0) {
380template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
388template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
396template<
typename Rep1,
typename Precision,
typename Rep2>
397 requires not_delta<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
401 return cd(
cd(
d).count() *
r);
405template<
typename Rep1,
typename Rep2,
typename Precision>
406 requires not_delta<Rep1> && std::convertible_to<const Rep1&, std::common_type_t<Rep1, Rep2>>
413template<
typename Rep1,
typename Precision,
typename Rep2>
414 requires not_delta<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
418 return cd(
cd(
d).count() /
s);
422template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
424 -> std::common_type_t<Rep1, Rep2> {
430template<
typename Rep1,
typename Precision,
typename Rep2>
431 requires not_delta<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>> &&
436 return cd(
cd(
d).count() %
s);
440template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
448template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
454template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
455 requires std::three_way_comparable<std::common_type_t<Rep1, Rep2>>
481 return std::to_string(
d.count()) +
"Δ°C";
485 return std::to_string(
d.count()) +
"Δd°C";
489 return std::to_string(
d.count()) +
"Δm°C";
493 return std::to_string(
d.count()) +
"Δ°F";
497 return std::to_string(
d.count()) +
"Δd°F";
501 return std::to_string(
d.count()) +
"Δm°F";
515 using offset = std::ratio<27315, 100>;
516 static constexpr const char*
suffix =
"°C";
525 using offset = std::ratio<0>;
526 static constexpr const char*
suffix =
"K";
535 using offset = std::ratio<45967, 180>;
536 static constexpr const char*
suffix =
"°F";
540template<
typename Scale,
typename Delta = delta<
int64_t>>
544template<
typename Scale1,
typename Delta1,
typename Scale2,
typename Delta2>
546 using from_prec =
typename Delta1::precision;
547 using to_prec =
typename Delta2::precision;
548 using from_off =
typename Scale1::offset;
549 using to_off =
typename Scale2::offset;
551 using prec_ratio = std::ratio_divide<from_prec, to_prec>;
552 using offset_diff = std::ratio_subtract<from_off, to_off>;
555 static constexpr bool value = prec_ratio::den == 1 && offset_in_target_units::den == 1;
558template<
typename Scale1,
typename Delta1,
typename Scale2,
typename Delta2>
568template<
typename Scale,
typename Delta>
585template<
typename ToTemp,
typename Scale,
typename Delta>
598template<
typename Scale,
typename Delta>
608 using rep =
typename Delta::rep;
619 template<
typename Rep2>
622 : _d(
static_cast<rep>(
r)) {}
632 template<
typename Delta2>
633 requires(!std::is_same_v<Delta, Delta2>) &&
641 template<
typename Delta2>
648 template<
typename Scale2,
typename Delta2>
649 requires(!std::is_same_v<temperature, temperature<Scale2, Delta2>>) &&
656 template<
typename Scale2,
typename Delta2>
657 requires(!std::is_same_v<temperature, temperature<Scale2, Delta2>>) &&
682 template<
typename Rep2,
typename Precision2>
688 template<
typename Rep2,
typename Precision2>
704template<
typename ToTemp,
typename Scale,
typename Delta>
706 using ToScale =
typename ToTemp::scale;
707 using ToDelta =
typename ToTemp::delta_type;
708 using to_rep =
typename ToDelta::rep;
710 if constexpr (std::is_same_v<Scale, ToScale> && std::is_same_v<Delta, ToDelta>) {
712 }
else if constexpr (std::is_same_v<Scale, ToScale>) {
715 using from_prec =
typename Delta::precision;
716 using from_off =
typename Scale::offset;
717 using to_prec =
typename ToDelta::precision;
718 using to_off =
typename ToScale::offset;
720 using offset_diff = std::ratio_subtract<from_off, to_off>;
721 using common_rep = std::common_type_t<typename Delta::rep, to_rep, intmax_t>;
725 using prec_ratio = std::ratio_divide<from_prec, to_prec>;
726 using offset_ratio = std::ratio_divide<offset_diff, to_prec>;
729 constexpr double pr =
static_cast<double>(prec_ratio::num) / prec_ratio::den;
730 constexpr double or_ =
static_cast<double>(offset_ratio::num) / offset_ratio::den;
735 static_cast<intmax_t>(prec_ratio::den) *
static_cast<intmax_t>(offset_ratio::den);
746template<
typename Scale,
typename Delta1,
typename Delta2>
749 using cd = std::common_type_t<Delta1, Delta2>;
754template<
typename Scale,
typename Delta1,
typename Delta2>
757 using cd = std::common_type_t<Delta1, Delta2>;
769template<
typename Scale,
typename Delta1,
typename Delta2>
771 -> std::common_type_t<Delta1, Delta2> {
772 using cd = std::common_type_t<Delta1, Delta2>;
773 return cd(
lhs.count() -
rhs.count());
777template<
typename Scale,
typename Delta1,
typename Rep2,
typename Precision2>
780 using result_delta = std::common_type_t<Delta1, delta<Rep2, Precision2>>;
785template<
typename Rep1,
typename Precision1,
typename Scale,
typename Delta2>
792template<
typename Scale,
typename Delta1,
typename Rep2,
typename Precision2>
795 using result_delta = std::common_type_t<Delta1, delta<Rep2, Precision2>>;
799template<
typename Scale,
typename Delta1,
typename Delta2>
801 return lhs.count() ==
rhs.count();
804template<
typename Scale,
typename Delta1,
typename Delta2>
805 requires std::three_way_comparable<std::common_type_t<typename Delta1::rep, typename Delta2::rep>>
807 return lhs.count() <=>
rhs.count();
830 return std::to_string(
t.count()) +
"°C";
834 return std::to_string(
t.count()) +
"d°C";
838 return std::to_string(
t.count()) +
"m°C";
842 return std::to_string(
t.count()) +
"K";
846 return std::to_string(
t.count()) +
"dK";
850 return std::to_string(
t.count()) +
"mK";
854 return std::to_string(
t.count()) +
"°F";
858 return std::to_string(
t.count()) +
"d°F";
862 return std::to_string(
t.count()) +
"m°F";
869template<
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
872 using common_precision = std::ratio<
874 (Precision1::den /
thermo::_gcd(Precision1::den, Precision2::den)) * Precision2::den>;
880template<
typename Scale,
typename Delta1,
typename Delta2>
885#if CONFIG_THERMO_STD_FORMAT
887struct formatter<
thermo::celsius> {
888 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
890 auto format(
thermo::celsius t, format_context& ctx)
const {
return std::format_to(ctx.out(),
"{}°C", t.
count()); }
894struct formatter<
thermo::decicelsius> {
895 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
898 return std::format_to(ctx.out(),
"{}d°C", t.
count());
903struct formatter<
thermo::millicelsius> {
904 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
907 return std::format_to(ctx.out(),
"{}m°C", t.
count());
912struct formatter<
thermo::kelvin> {
913 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
915 auto format(
thermo::kelvin t, format_context& ctx)
const {
return std::format_to(ctx.out(),
"{}K", t.
count()); }
919struct formatter<
thermo::decikelvin> {
920 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
923 return std::format_to(ctx.out(),
"{}dK", t.
count());
928struct formatter<
thermo::millikelvin> {
929 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
932 return std::format_to(ctx.out(),
"{}mK", t.
count());
937struct formatter<
thermo::fahrenheit> {
938 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
941 return std::format_to(ctx.out(),
"{}°F", t.
count());
946struct formatter<
thermo::decifahrenheit> {
947 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
950 return std::format_to(ctx.out(),
"{}d°F", t.
count());
955struct formatter<
thermo::millifahrenheit> {
956 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
959 return std::format_to(ctx.out(),
"{}m°F", t.
count());
964struct formatter<
thermo::delta<int64_t>> {
965 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
968 return std::format_to(ctx.out(),
"{}Δ°C", d.
count());
973struct formatter<
thermo::delta<int64_t, std::deci>> {
974 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
977 return std::format_to(ctx.out(),
"{}Δd°C", d.
count());
982struct formatter<
thermo::delta<int64_t, std::milli>> {
983 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
986 return std::format_to(ctx.out(),
"{}Δm°C", d.
count());
991struct formatter<
thermo::delta_fahrenheit> {
992 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
995 return std::format_to(ctx.out(),
"{}Δ°F", d.
count());
1000struct formatter<
thermo::delta_decifahrenheit> {
1001 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
1004 return std::format_to(ctx.out(),
"{}Δd°F", d.
count());
1009struct formatter<
thermo::delta_millifahrenheit> {
1010 constexpr auto parse(format_parse_context& ctx) {
return ctx.begin(); }
1013 return std::format_to(ctx.out(),
"{}Δm°F", d.
count());
1027template<
unsigned long long Value,
unsigned long long Power>
1029 static constexpr unsigned long long value = 10 * pow10<Value, Power - 1>::value;
1032template<
unsigned long long Value>
1033struct pow10<Value, 0> {
1034 static constexpr unsigned long long value = Value;
1037template<
char... Digits>
1040template<
char D,
char... Rest>
1041struct parse_int<D, Rest...> {
1042 static_assert(D >=
'0' && D <=
'9',
"invalid digit");
1043 static constexpr unsigned long long value = pow10<D -
'0',
sizeof...(Rest)>::value + parse_int<Rest...>::value;
1047struct parse_int<D> {
1048 static_assert(D >=
'0' && D <=
'9',
"invalid digit");
1049 static constexpr unsigned long long value = D -
'0';
1052template<
typename Delta,
char... Digits>
1053constexpr Delta check_overflow() {
1054 using parsed = parse_int<Digits...>;
1055 constexpr typename Delta::rep repval = parsed::value;
1057 repval >= 0 &&
static_cast<unsigned long long>(repval) == parsed::value,
1058 "literal value cannot be represented by delta type"
1060 return Delta(repval);
1067template<
char... Digits>
1069 return thermo::celsius(detail::check_overflow<thermo::delta_celsius, Digits...>());
1073template<
char... Digits>
1075 return thermo::decicelsius(detail::check_overflow<thermo::delta_decicelsius, Digits...>());
1079template<
char... Digits>
1085template<
char... Digits>
1087 return thermo::kelvin(detail::check_overflow<thermo::delta_kelvin, Digits...>());
1091template<
char... Digits>
1093 return thermo::decikelvin(detail::check_overflow<thermo::delta_decikelvin, Digits...>());
1097template<
char... Digits>
1099 return thermo::millikelvin(detail::check_overflow<thermo::delta_millikelvin, Digits...>());
1103template<
char... Digits>
1105 return thermo::fahrenheit(detail::check_overflow<thermo::delta_fahrenheit, Digits...>());
1109template<
char... Digits>
1115template<
char... Digits>
1121template<
char... Digits>
1127template<
char... Digits>
1133template<
char... Digits>
1139template<
char... Digits>
1145template<
char... Digits>
1151template<
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++()
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.
temperature< fahrenheit_scale, delta< int64_t, std::ratio< 5, 900 > > > 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.
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.
delta< int64_t, std::ratio< 5, 900 > > delta_millifahrenheit
Delta with 0.001°F precision.
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)
std::string to_string(delta< int64_t > d)
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.