Unit Conversion and Dimensional Analysis Library 3.6.1
A compile-time, header-only C++23 dimensional-analysis library
Loading...
Searching...
No Matches
core.h
Go to the documentation of this file.
1//--------------------------------------------------------------------------------------------------
2//
3// UnitConversion: A compile-time c++23 unit conversion library with no dependencies
4//
5//--------------------------------------------------------------------------------------------------
6//
7// The MIT License (MIT)
8//
9// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
10// and associated documentation files (the "Software"), to deal in the Software without
11// restriction, including without limitation the rights to use, copy, modify, merge, publish,
12// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
13// Software is furnished to do so, subject to the following conditions:
14//
15// The above copyright notice and this permission notice shall be included in all copies or
16// substantial portions of the Software.
17//
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
19// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23//
24//--------------------------------------------------------------------------------------------------
25//
26// Copyright (c) 2016 Nic Holthaus
27//
28//--------------------------------------------------------------------------------------------------
29//
30// ATTRIBUTION:
31// Parts of this work have been adapted from:
32// http://stackoverflow.com/questions/35069778/create-comparison-trait-for-template-classes-whose-parameters-are-in-a-different
33// http://stackoverflow.com/questions/28253399/check-traits-for-all-variadic-template-arguments/28253503
34// http://stackoverflow.com/questions/36321295/rational-approximation-of-square-root-of-stdratio-at-compile-time?noredirect=1#comment60266601_36321295
35// https://github.com/swatanabe/cppnow17-units
36//
37//--------------------------------------------------------------------------------------------------
38//
42//
43//--------------------------------------------------------------------------------------------------
44
45#pragma once
46
47#ifndef UNIT_CORE_H
48#define UNIT_CORE_H
49
50#ifndef UNIT_LIB_DEFAULT_TYPE
51#define UNIT_LIB_DEFAULT_TYPE double
52#endif
53
54//--------------------
55// INCLUDES
56//--------------------
57
58#include "core.h"
59#include <chrono>
60#include <cmath>
61#include <concepts>
62#include <cstddef>
63#include <cstdint>
64#include <functional>
65#include <limits>
66#include <numeric>
67#include <ratio>
68#include <type_traits>
69#include <utility>
70#include <version>
71
72// ---------------------------------------------------------------------------------------------------------------------
73// TEXT-FEATURE CONFIGURATION (opt-out; full capability is the default)
74// ---------------------------------------------------------------------------------------------------------------------
75// Out of the box every text feature is ON: stream inserters, to_string, and std::format (where <format> is
76// available). Consumers opt OUT with the DISABLE_ macros. The ENABLE_* macros are derived internal switches
77// — do not define them directly, with the single exception of UNIT_LIB_ENABLE_FORMAT (the documented opt-in
78// that restores std::format under UNIT_LIB_DISABLE_IOSTREAM).
79//
80// UNIT_LIB_DISABLE_IOSTREAM Drops the stream inserters. For BACKWARD COMPATIBILITY this also drops
81// to_string, <string>, and std::format: a legacy iostream-disabled build has
82// always been the lean, string-free build, and stays byte-for-byte that. To
83// keep std::format while dropping streams, ALSO define UNIT_LIB_ENABLE_FORMAT.
84// UNIT_LIB_DISABLE_FORMAT Drops only std::format support; iostream and to_string remain.
85// UNIT_LIB_DISABLE_STRING The leanest build: forbids <string>, and therefore implies both of the above.
86//
87// std::format support additionally requires the standard library to provide <format> (__cpp_lib_format).
88
89#if defined(UNIT_LIB_DISABLE_STRING)
90#if !defined(UNIT_LIB_DISABLE_IOSTREAM)
91#define UNIT_LIB_DISABLE_IOSTREAM
92#endif
93#if !defined(UNIT_LIB_DISABLE_FORMAT)
94#define UNIT_LIB_DISABLE_FORMAT
95#endif
96#endif
97
98// std::format: on by default when <format> exists; off if explicitly disabled or if string is disabled; off
99// alongside iostream UNLESS the caller opts back in with UNIT_LIB_ENABLE_FORMAT.
100#if !defined(UNIT_LIB_DISABLE_FORMAT) && !defined(UNIT_LIB_DISABLE_STRING) && defined(__cpp_lib_format) && __cpp_lib_format >= 201907L && \
101 (!defined(UNIT_LIB_DISABLE_IOSTREAM) || defined(UNIT_LIB_ENABLE_FORMAT))
102#if !defined(UNIT_LIB_ENABLE_FORMAT)
103#define UNIT_LIB_ENABLE_FORMAT
104#endif
105#else
106// If format cannot be enabled, ensure a stray UNIT_LIB_ENABLE_FORMAT does not leak through.
107#undef UNIT_LIB_ENABLE_FORMAT
108#endif
109
110// The value stringifier + unit-label builders (and <string>) exist whenever any text feature — the stream
111// inserters or std::format — is compiled in.
112#if !defined(UNIT_LIB_DISABLE_STRING) && (!defined(UNIT_LIB_DISABLE_IOSTREAM) || defined(UNIT_LIB_ENABLE_FORMAT))
113#define UNIT_LIB_ENABLE_STRING
114#endif
115
116#if defined(UNIT_LIB_ENABLE_STRING)
117#include <string>
118#endif
119
120#if defined(UNIT_LIB_ENABLE_FORMAT)
121#include <format>
122#include <string_view>
123#endif
124
125#if defined(UNIT_LIB_ENABLE_STRING)
126#include <clocale>
127
128//------------------------------
129// VALUE STRINGIFIER
130//------------------------------
131
132namespace units::detail
133{
134 template<typename T>
135 requires std::is_arithmetic_v<T> // numbers only: a named unit's associated namespace is units::detail, so an
136 // unconstrained overload here would be an ADL candidate for to_string(someUnit)
137 std::string to_string(const T& t)
138 {
139 std::string str{std::to_string(t)};
140
141 if constexpr (std::is_floating_point_v<T>)
142 {
143 unsigned int offset{1};
144
145 // remove trailing decimal points for integer value units. Locale aware!
146 std::lconv* lc = std::localeconv();
147 char decimalPoint = *lc->decimal_point;
148 if (str.find_last_not_of('0') == str.find(decimalPoint))
149 {
150 offset = 0;
151 }
152 str.erase(str.find_last_not_of('0') + offset, std::string::npos);
153 }
154 return str;
155 }
156} // namespace units::detail
157
158#endif // UNIT_LIB_ENABLE_STRING
159
160#if !defined(UNIT_LIB_DISABLE_IOSTREAM)
161#include <sstream>
162#endif
163
164//------------------------------
165// FORWARD DECLARATIONS
166//------------------------------
167
168namespace units
169{
170 struct linear_scale;
171 struct decibel_scale;
172
173 template<class Unit>
175 {
176 static constexpr const char* value = nullptr;
177 };
178
179 template<class Unit>
181 {
182 static constexpr const char* value = nullptr;
183 };
184
185 template<class Unit>
186 inline constexpr const char* unit_name_v = unit_name<Unit>::value;
187
188 template<class Unit>
189 inline constexpr const char* unit_abbreviation_v = unit_abbreviation<Unit>::value;
190
191 namespace detail
192 {
193 inline constexpr UNIT_LIB_DEFAULT_TYPE PI_VAL = 3.14159265358979323846264338327950288419716939937510;
194 }
195
196 namespace traits
197 {
198 template<typename T>
200 } // namespace traits
201} // namespace units
202
203//------------------------------
204// MACROS
205//------------------------------
206
221#define UNIT_ADD_STRONG_CONVERSION_FACTOR(namespaceName, namePlural, /*conversion factor*/...) \
222 inline namespace namespaceName \
223 { \
224 struct namePlural##_ : __VA_ARGS__ \
225 { \
226 }; \
227 } \
228 namespace detail \
229 { \
230 \
231 \
232 \
233 ::units::namespaceName::namePlural##_ strong_name(__VA_ARGS__*); \
234 }
235
246#define UNIT_ADD_UNIT_DEFINITION(namespaceName, namePlural, /*conversionFactor*/...) \
247 inline namespace namespaceName \
248 { \
249 UNIT_ADD_SCALED_UNIT_DEFINITION(namePlural, ::units::linear_scale, __VA_ARGS__) \
250 }
251
267#define UNIT_ADD_SCALED_UNIT_DEFINITION(unitName, scale, /*conversionFactor*/...) \
268 \
269\
270 template<class Underlying = UNIT_LIB_DEFAULT_TYPE> \
271 struct unitName : ::units::unit<traits::strong_t<__VA_ARGS__>, Underlying, scale> \
272 { \
273 using base = ::units::unit<traits::strong_t<__VA_ARGS__>, Underlying, scale>; \
274 using base::base; \
275 /* Keep the named class TRIVIAL (a load-bearing property of the unit type — memcpy-able, zero overhead): */ \
276 /* explicitly default the special members. Declaring the converting ctor below would otherwise suppress the */ \
277 /* trivial default ctor, and inheriting ctors leaves the special members implicit; defaulting them restores */ \
278 /* std::is_trivial. */ \
279 unitName() = default; \
280 unitName(const unitName&) = default; \
281 unitName(unitName&&) = default; \
282 unitName& operator=(const unitName&) = default; \
283 unitName& operator=(unitName&&) = default; \
284 constexpr unitName(const base& other) noexcept : base(other) {} \
285 /* Explicit consteval forwarding of the base's compile-time narrowing converting constructor. The base is */ \
286 /* constructed directly (`base(rhs)`), so the named class does not rely on `using base::base` to SYNTHESIZE an */\
287 /* inheriting-constructor wrapper for this consteval ctor. GCC 13's constant evaluator mis-handles that */ \
288 /* synthesized inheriting wrapper — it treats the base subobject as uninitialized (accessing uninitialized */ \
289 /* member, this is not a constant expression) — while an explicit derived ctor evaluates correctly. The */ \
290 /* base's own requires-clause gates viability; the derived constraint keeps this a candidate only for the */ \
291 /* narrowing the base ctors accept — a floating-point source, or a finer integral source that is an exact */ \
292 /* whole number of this integral unit. `base(rhs)` selects whichever base consteval ctor matches the source. */ \
293 template<::units::ConversionFactorType Cf, ::units::ArithmeticType Ty, ::units::NumericalScaleType<Ty> Ns> \
294 requires(::units::traits::is_same_dimension_unit_v<::units::unit<Cf, Ty, Ns>, base> && \
295 !::units::detail::is_losslessly_convertible_unit<::units::unit<Cf, Ty, Ns>, base> && \
296 (::std::is_floating_point_v<Ty> || ::std::is_integral_v<Ty>) && ::std::is_integral_v<Underlying>) \
297 consteval unitName(const ::units::unit<Cf, Ty, Ns>& rhs) : base(rhs) {} \
298 /* Forward a scalar assignment to the base's operator= so the dimensionless '= 0.30' path (which the derived */ \
299 /* class would otherwise route through the raw-value converting ctor, off by the CF ratio) is used. Templated */\
300 /* + constrained to arithmetic so it never competes with unit-to-unit assignment (that stays the base's job). */\
301 template<class Rhs> \
302 requires ::std::is_arithmetic_v<Rhs> \
303 constexpr unitName& operator=(const Rhs& rhs) noexcept \
304 { \
305 base::operator=(rhs); \
306 return *this; \
307 } \
308 \
309 \
310 template<class NewUnderlying> \
311 using rebind = unitName<NewUnderlying>; \
312 }; \
313 \
314\
315 \
316 \
317 template<class Arg> \
318 requires ::std::is_arithmetic_v<Arg> \
319 unitName(Arg) -> unitName<Arg>; \
320\
321 \
322 unitName() -> unitName<UNIT_LIB_DEFAULT_TYPE>; \
323 \
324 \
325\
326 \
327 \
328 template<class OtherUnit> \
329 requires(::units::traits::is_unit<OtherUnit>::value && \
330 ::units::traits::is_same_dimension_unit_v<OtherUnit, \
331 ::units::unit<traits::strong_t<__VA_ARGS__>, typename ::units::traits::unit_traits<OtherUnit>::underlying_type, scale>>) \
332 unitName(const OtherUnit&) -> unitName<::units::detail::deduced_named_underlying_t<OtherUnit, traits::strong_t<__VA_ARGS__>, scale>>;\
333 \
334 \
335 \
336 template<class Rep, class Period> \
337 unitName(const ::std::chrono::duration<Rep, Period>&) -> unitName<UNIT_LIB_DEFAULT_TYPE>; \
338
339
339 * @def UNIT_ADD_NAME(namespaceName,namePlural,abbreviation)
340 * @brief Macro for generating constexpr names/abbreviations for units.
341 * @details The macro generates names for units. E.g. name() of 1_m would be "meter", and
342 * abbreviation would be "m".
343 * @param namespaceName namespace in which the new units will be encapsulated. All literal values
344 * are placed in the `units::literals` namespace.
345 * @param namePlural - plural version of the unit name, e.g. 'meters'
346 * @param abbreviation - abbreviated unit name, e.g. 'm'
347 */
348#define UNIT_ADD_NAME(namespaceName, namePlural, abbrev) \
349 template<class Underlying> \
350 struct unit_name<namespaceName::namePlural<Underlying>> \
351 { \
352 static constexpr const char* value = #namePlural; \
353 }; \
354 \
355 template<class Underlying> \
356 struct unit_abbreviation<namespaceName::namePlural<Underlying>> \
357 { \
358 static constexpr const char* value = #abbrev; \
359 };
369#define UNIT_REGISTER_NAMED_CLASS(namespaceName, namePlural) \
370 namespace detail \
371 { \
372 /* Keyed on BOTH the conversion_factor AND the numerical scale: the linear and decibel forms of a unit share */ \
373 /* one conversion_factor (watts_ for both watts and dBW) and differ only by scale, so scale must disambiguate */\
374 /* the reverse map (else watts vs dBW collide). Declared, never defined (decltype-only). */ \
375 ::units::namespaceName::namePlural<UNIT_LIB_DEFAULT_TYPE> named_class_of( \
376 typename ::units::namespaceName::namePlural<>::conversion_factor*, \
377 typename ::units::namespaceName::namePlural<>::numerical_scale_type*); \
378 }
379
385 * @param namespaceName namespace in which the new units will be encapsulated. All literal values
386 * are placed in the `units::literals` namespace.
387 * @param namePlural - plural version of the unit name, e.g. 'meters'
388 * @param abbreviation - abbreviated unit name, e.g. 'm'
389 * @note When UNIT_NO_LITERAL_SUPPORT is defined, the macro does not generate any code
390 */
391#ifdef UNIT_NO_LITERAL_SUPPORT
392#define UNIT_ADD_LITERALS(namespaceName, namePlural, abbreviation)
393#else
394#define UNIT_ADD_LITERALS(namespaceName, namePlural, abbreviation) \
395 namespace literals \
396 { \
397 /* A literal is always floating-point. It uses the library default type when that is a floating-point */ \
398 /* type, and its floating-point promotion otherwise, so a literal is never integer-backed even if the */ \
399 /* default representation is integral. */ \
400 constexpr namespaceName::namePlural<::units::detail::floating_point_promotion_t<UNIT_LIB_DEFAULT_TYPE>> operator""_##abbreviation(long double d) noexcept \
401 { \
402 return namespaceName::namePlural<::units::detail::floating_point_promotion_t<UNIT_LIB_DEFAULT_TYPE>>(static_cast<::units::detail::floating_point_promotion_t<UNIT_LIB_DEFAULT_TYPE>>(d)); \
403 } \
404 /* An integer literal (5_m) yields the same floating-point type as 5.0_m. A literal is a value a user */ \
405 /* writes inline; deducing an integer representation from it silently opts into integer arithmetic */ \
406 /* (5_m / 2_m == 0), which is rarely intended, and diverges from the unit constant form (5 * m is always */ \
407 /* floating-point). An integer-backed quantity is still available explicitly (namePlural<int>(5)) or by */ \
408 /* CTAD from an integer argument (namePlural(5)). */ \
409 constexpr namespaceName::namePlural<::units::detail::floating_point_promotion_t<UNIT_LIB_DEFAULT_TYPE>> operator""_##abbreviation(unsigned long long d) noexcept \
410 { \
411 return namespaceName::namePlural<::units::detail::floating_point_promotion_t<UNIT_LIB_DEFAULT_TYPE>>(static_cast<::units::detail::floating_point_promotion_t<UNIT_LIB_DEFAULT_TYPE>>(d)); \
412 } \
413 }
414#endif
415
416/**
417 * @def UNIT_ADD_DECIBEL_LITERALS(namespaceName, namePlural, abbreviation)
418 * @brief Like UNIT_ADD_LITERALS but emits only the floating-point literal.
419 * @details A decibel-scale unit requires a floating-point underlying type, so no integer literal
420 * (which would form a `<int>` unit) is generated.
421 */
422#ifdef UNIT_NO_LITERAL_SUPPORT
423#define UNIT_ADD_DECIBEL_LITERALS(namespaceName, namePlural, abbreviation)
424#else
425#define UNIT_ADD_DECIBEL_LITERALS(namespaceName, namePlural, abbreviation) \
426 namespace literals \
427 { \
428 constexpr namespaceName::namePlural<double> operator""_##abbreviation(long double d) noexcept \
429 { \
430 return namespaceName::namePlural<double>(static_cast<double>(d)); \
431 } \
432 }
433#endif
434
435#define UNIT_ADD_CONSTANT(namespaceName, namePlural, abbreviation) static constexpr namespaceName::namePlural abbreviation{1.0};
436
447 * @param namePlural - plural version of the unit name, e.g. 'meters'
448 * @param abbreviation - abbreviated unit name, e.g. 'm'
449 * @param ... - the conversion factor definition for the unit type. Taken as variadic
450 * arguments because they contain commas in the macro definition. The complete __VA_ARGS__
451 * represents the full conversion factor type. e.g. `meters<>`.
452 * @note a variadic template is used for the definition to allow templates with
453 * commas to be easily expanded. All the variadic 'arguments' should together
454 * comprise the unit definition.
455 */
456#define UNIT_ADD(namespaceName, namePlural, abbreviation, /*conversionFactor*/...) \
457 UNIT_ADD_STRONG_CONVERSION_FACTOR(namespaceName, namePlural, __VA_ARGS__) \
458 UNIT_ADD_UNIT_DEFINITION(namespaceName, namePlural, __VA_ARGS__) \
459 UNIT_ADD_NAME(namespaceName, namePlural, abbreviation) \
460 UNIT_REGISTER_NAMED_CLASS(namespaceName, namePlural) \
461 UNIT_ADD_LITERALS(namespaceName, namePlural, abbreviation) \
462 UNIT_ADD_CONSTANT(namespaceName, namePlural, abbreviation)
463
464/**
465 * @def UNIT_ADD_DECIBEL(namespaceName, namePlural, abbreviation)
466 * @brief Macro to create decibel container and literals for an existing unit type.
467 * @details This macro generates the decibel unit container, cout overload, and literal definitions.
468 * @param namespaceName namespace in which the new units will be encapsulated. All literal values
469 * are placed in the `units::literals` namespace.
470 * @param namePlural plural version of the dimension name, e.g. 'watts'
471 * @param abbreviation - abbreviated decibel unit name, e.g. 'dBW'
472 */
473#define UNIT_ADD_DECIBEL(namespaceName, namePlural, abbreviation) \
474 inline namespace namespaceName \
475 { \
476 UNIT_ADD_SCALED_UNIT_DEFINITION(abbreviation, ::units::decibel_scale, typename ::units::namespaceName::namePlural<>::conversion_factor) \
477 } \
478 UNIT_ADD_NAME(namespaceName, abbreviation, abbreviation) \
479 UNIT_REGISTER_NAMED_CLASS(namespaceName, abbreviation) \
480 UNIT_ADD_DECIBEL_LITERALS(namespaceName, abbreviation, abbreviation)
481
488 * (`void f(Velocity auto)`) rather than a concrete named type. Being dimension-keyed, the concept
489 * classifies a computed result consistently regardless of which dimension headers a translation
490 * unit included. This macro comprises all the boilerplate code necessary to do so. The C
491 * preprocessor cannot uppercase a token, so the PascalCase concept name is supplied as a separate
492 * argument rather than derived from `unitdimension`.
493 * @param unitdimension The name of the dimension of unit, e.g. length or mass.
494 * @param ConceptName The PascalCase name of the emitted concept, e.g. Length or Mass.
495 */
496
497#define UNIT_ADD_DIMENSION_TRAIT(unitdimension, ConceptName) \
498 \
499 \
500 \
501 \
502 \
503 namespace traits \
504 { \
505 template<typename T> \
506 struct is_##unitdimension##_unit : ::units::detail::has_dimension_of<std::decay_t<T>, units::dimension::unitdimension> \
507 { \
508 }; \
509 template<typename T> \
510 inline constexpr bool is_##unitdimension##_unit_v = is_##unitdimension##_unit<T>::value; \
511 } \
512 \
513 \
514 \
515 \
516 \
517 template<typename T> \
518 concept ConceptName = ::units::traits::is_##unitdimension##_unit_v<std::decay_t<T>>;
519
529 * @param namePlural - plural version of the unit name, e.g. 'meters'
530 * @param abbreviation - abbreviated unit name, e.g. 'm'
531 * @param ... - the conversion factor definition for the unit type. Taken as variadic
532 * arguments because they contain commas in the macro definition. The complete __VA_ARGS__
533 * represents the full conversion factor type. e.g. `meters<>`.
534 * @note a variadic template is used for the definition to allow templates with
535 * commas to be easily expanded. All the variadic 'arguments' should together
536 * comprise the unit definition.
537 */
538#define UNIT_ADD_WITH_METRIC_PREFIXES(namespaceName, namePlural, abbreviation, /*conversionFactor*/...) \
539 UNIT_ADD(namespaceName, namePlural, abbreviation, __VA_ARGS__) \
540 UNIT_ADD(namespaceName, femto##namePlural, f##abbreviation, femto<namePlural<>>) \
541 UNIT_ADD(namespaceName, pico##namePlural, p##abbreviation, pico<namePlural<>>) \
542 UNIT_ADD(namespaceName, nano##namePlural, n##abbreviation, nano<namePlural<>>) \
543 UNIT_ADD(namespaceName, micro##namePlural, u##abbreviation, micro<namePlural<>>) \
544 UNIT_ADD(namespaceName, milli##namePlural, m##abbreviation, milli<namePlural<>>) \
545 UNIT_ADD(namespaceName, centi##namePlural, c##abbreviation, centi<namePlural<>>) \
546 UNIT_ADD(namespaceName, deci##namePlural, d##abbreviation, deci<namePlural<>>) \
547 UNIT_ADD(namespaceName, deca##namePlural, da##abbreviation, deca<namePlural<>>) \
548 UNIT_ADD(namespaceName, hecto##namePlural, h##abbreviation, hecto<namePlural<>>) \
549 UNIT_ADD(namespaceName, kilo##namePlural, k##abbreviation, kilo<namePlural<>>) \
550 UNIT_ADD(namespaceName, mega##namePlural, M##abbreviation, mega<namePlural<>>) \
551 UNIT_ADD(namespaceName, giga##namePlural, G##abbreviation, giga<namePlural<>>) \
552 UNIT_ADD(namespaceName, tera##namePlural, T##abbreviation, tera<namePlural<>>) \
553 UNIT_ADD(namespaceName, peta##namePlural, P##abbreviation, peta<namePlural<>>)
554
564 * @param namePlural - plural version of the unit name, e.g. 'bytes'
565 * @param abbreviation - abbreviated unit name, e.g. 'B'
566 * @param ... - the conversion factor definition for the unit type. Taken as variadic
567 * arguments because they contain commas in the macro definition. The complete __VA_ARGS__
568 * represents the full conversion factor type. e.g. `meters<>`.
569 * @note a variadic template is used for the definition to allow templates with
570 * commas to be easily expanded. All the variadic 'arguments' should together
571 * comprise the unit definition.
572 */
573#define UNIT_ADD_WITH_METRIC_AND_BINARY_PREFIXES(namespaceName, namePlural, abbreviation, /*conversionFactor*/...) \
574 UNIT_ADD_WITH_METRIC_PREFIXES(namespaceName, namePlural, abbreviation, __VA_ARGS__) \
575 UNIT_ADD(namespaceName, kibi##namePlural, Ki##abbreviation, kibi<namePlural<>>) \
576 UNIT_ADD(namespaceName, mebi##namePlural, Mi##abbreviation, mebi<namePlural<>>) \
577 UNIT_ADD(namespaceName, gibi##namePlural, Gi##abbreviation, gibi<namePlural<>>) \
578 UNIT_ADD(namespaceName, tebi##namePlural, Ti##abbreviation, tebi<namePlural<>>) \
579 UNIT_ADD(namespaceName, pebi##namePlural, Pi##abbreviation, pebi<namePlural<>>) \
580 UNIT_ADD(namespaceName, exbi##namePlural, Ei##abbreviation, exbi<namePlural<>>)
581
582//--------------------
583// UNITS NAMESPACE
584//--------------------
585
590namespace units
591{
592 //----------------------------------
593 // DOXYGEN
594 //----------------------------------
595
601
608
614
619
624
629
634
639
644
650
656
662
673
674 //------------------------------
675 // DETECTION IDIOM
676 //------------------------------
677 // DOXYGEN IGNORE
679 namespace detail
680 {
687 template<class Default, class AlwaysVoid, template<class...> class Op, class... Args>
688 struct detector
689 {
690 using value_t = std::false_type;
691 using type = Default;
692 };
693
694 template<class Default, template<class...> class Op, class... Args>
695 struct detector<Default, std::void_t<Op<Args...>>, Op, Args...>
696 {
697 using value_t = std::true_type;
698 using type = Op<Args...>;
699 };
700
701 struct nonesuch
702 {
703 nonesuch() = delete;
704 ~nonesuch() = delete;
705 nonesuch(const nonesuch&) = delete;
706 void operator=(const nonesuch&) = delete;
707 };
708
709 template<template<class...> class Op, class... Args>
710 using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;
711
712 template<template<class...> class Op, class... Args>
713 inline constexpr bool is_detected_v = is_detected<Op, Args...>::value;
714
715 template<template<class...> class Op, class... Args>
716 using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
717
718 template<class Default, template<class...> class Op, class... Args>
719 using detected_or = detector<Default, void, Op, Args...>;
720
721 template<class Default, template<class...> class Op, class... Args>
722 using detected_or_t = typename detected_or<Default, Op, Args...>::type;
723
724 template<class Expected, template<class...> class Op, class... Args>
725 using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
726
727 template<class Expected, template<class...> class Op, class... Args>
728 inline constexpr bool is_detected_exact_v = is_detected_exact<Expected, Op, Args...>::value;
729
730 template<class To, template<class...> class Op, class... Args>
731 using is_detected_convertible = std::is_convertible<detected_t<Op, Args...>, To>;
732
733 template<class To, template<class...> class Op, class... Args>
734 inline constexpr bool is_detected_convertible_v = is_detected_convertible<To, Op, Args...>::value;
735 } // namespace detail // END DOXYGEN IGNORE
737
738 //------------------------------
739 // RATIO TRAITS
740 //------------------------------
741
746
747 namespace traits
748 { // DOXYGEN IGNORE
750 namespace detail
751 {
752 template<class T>
753 struct is_ratio_impl : std::false_type
754 {
755 };
756
757 template<std::intmax_t N, std::intmax_t D>
758 struct is_ratio_impl<std::ratio<N, D>> : std::true_type
759 {
760 };
761 } // namespace detail // END DOXYGEN IGNORE
763
769 template<class T>
770 using is_ratio = detail::is_ratio_impl<T>;
771
772 template<class T>
773 inline constexpr bool is_ratio_v = is_ratio<T>::value;
774 } // namespace traits
775
776 //------------------------------
777 // CONVERSION FACTOR TRAITS
778 //------------------------------
779
783 namespace traits
784 {
785#ifdef FOR_DOXYGEN_PURPOSES_ONLY
793 template<class T>
794 struct conversion_factor_traits
795 {
796 typedef typename T::dimension_type dimension_type;
799 typedef typename T::conversion_ratio conversion_ratio;
801 typedef typename T::pi_exponent_ratio pi_exponent_ratio;
803 typedef typename T::translation_ratio translation_ratio;
805 };
806#endif // DOXYGEN IGNORE
811 template<class T, typename = void>
812 struct conversion_factor_traits
813 {
814 using dimension_type = void;
815 using conversion_ratio = void;
816 using pi_exponent_ratio = void;
817 using translation_ratio = void;
818 };
819
820 template<class T>
821 struct conversion_factor_traits<T, std::void_t<typename T::dimension_type, typename T::conversion_ratio, typename T::pi_exponent_ratio, typename T::translation_ratio>>
822 {
823 using dimension_type = typename T::dimension_type;
826 using conversion_ratio = typename T::conversion_ratio;
828 using pi_exponent_ratio = typename T::pi_exponent_ratio;
830 using translation_ratio = typename T::translation_ratio;
833 };
834 // END DOXYGEN IGNORE
836 } // namespace traits
837 // DOXYGEN IGNORE
839 namespace detail
840 {
845 struct _conversion_factor
846 {
847 };
848 } // namespace detail
849 // END DOXYGEN IGNORE
851
852 namespace traits
853 {
860 template<class T>
861 using is_conversion_factor = typename std::is_base_of<units::detail::_conversion_factor, T>::type;
862
863 template<class T>
864 inline constexpr bool is_conversion_factor_v = is_conversion_factor<T>::value;
865 } // namespace traits
866 // end of TypeTraits
868
869 //------------------------------
870 // UNIT TRAITS
871 //------------------------------
872
873 namespace detail
874 {
879 struct _unit
880 {
881 };
882
885 * other trait.
886 * @details `std::is_base_of<Base, T>` is ill-formed when `T` is an incomplete, non-same class type (a
887 * conforming library — libc++ — rejects it). `is_unit` must stay usable as a SFINAE probe on
888 * arbitrary foreign types, and one such type is a standard-library class caught mid-definition:
889 * libc++'s `<chrono>` evaluates every `std::common_type` partial specialization while
890 * `std::chrono::duration` is still incomplete, which would instantiate `is_base_of<_unit,
891 * duration>` on the incomplete `duration`. Gating the base-of test on completeness avoids that.
892 */
893 template<class T, class = void>
894 struct is_complete : std::false_type
895 {
896 };
897
898 template<class T>
899 struct is_complete<T, std::void_t<decltype(sizeof(T))>> : std::true_type
901 };
902
903
905 * instantiating `is_base_of` (which is ill-formed on an incomplete non-same class type). Only a
906 * complete type reaches the `is_base_of` test in the specialization below.
907 */
908 template<class T, bool = is_complete<T>::value>
909 struct is_unit_impl : std::false_type
910 {
911 };
912
913 template<class T>
914 struct is_unit_impl<T, true> : std::is_base_of<_unit, T>::type
915 {
916 };
917
933 template<class ConversionFactor>
934 ConversionFactor strong_name(ConversionFactor*, ...);
935 } // namespace detail
936
937 namespace traits
938 {
940
945 template<class T>
946 struct is_unit : units::detail::is_unit_impl<T>::type
947 {
948 };
949
950 template<class T>
951 inline constexpr bool is_unit_v = is_unit<T>::value && !std::is_arithmetic_v<T>;
952 // DOXYGEN IGNORE
954 namespace detail
955 {
956 template<class NumericalScale>
957 struct invocable_scale
958 {
959 template<class T>
960 requires std::is_same_v<decltype(NumericalScale::linearize(T{})), decltype(NumericalScale::scale(T{}))>
961 decltype(NumericalScale::scale(T{})) operator()(T)
962 {
963 return scale(T{});
964 }
965 };
966 } // namespace detail // END DOXYGEN IGNORE
968
980 template<class T, class Ret>
981 using is_numerical_scale = std::is_invocable_r<Ret, detail::invocable_scale<T>, Ret>;
982
983 template<class T, class Ret>
984 inline constexpr bool is_numerical_scale_v = is_numerical_scale<T, Ret>::value;
985 } // namespace traits
986
987 //------------------------------
988 // CONCEPTS
989 //------------------------------
990
995 template<typename T>
996 concept ArithmeticType = std::is_arithmetic_v<T>;
997
1002 template<typename T>
1003 concept NonArithmeticType = !std::is_arithmetic_v<T>;
1004
1009 template<typename T>
1010 concept RatioType = traits::is_ratio_v<T>;
1011
1016 template<typename T>
1017 concept ConversionFactorType = traits::is_conversion_factor_v<T>;
1018
1023 template<typename Scale, typename T>
1024 concept NumericalScaleType = traits::is_numerical_scale_v<Scale, T>;
1025
1030 template<typename T>
1031 concept UnitType = traits::is_unit_v<T>;
1032
1037 template<typename T>
1038 concept DimensionedUnitType = traits::is_unit_v<T> && !traits::is_dimensionless_unit<T>::value;
1039
1044 template<typename T>
1045 concept DimensionlessUnitType = traits::is_unit_v<T> && traits::is_dimensionless_unit<T>::value;
1046
1047 namespace traits
1048 {
1049 // forward declaration
1050 template<UnitType U1, UnitType U2>
1051 struct is_same_dimension_unit;
1052 } // namespace traits
1053
1058 template<typename UnitTo, typename UnitFrom>
1060
1061 //------------------------------
1062 // STRONG UNIT TYPES
1063 //------------------------------
1064
1065 namespace traits
1066 {
1075 * point (`units::detail::strong_name`), NOT an explicit specialization of `strong`: a named
1076 * type is discovered by overload resolution over the `conversion_factor`'s associated
1077 * namespace at the point `strong_t<T>` is instantiated. This deliberately avoids the
1078 * "explicit specialization after implicit instantiation" ordering trap (#357) — forming an
1079 * expression that reduces to a not-yet-included dimension no longer bakes in a decision a
1080 * later header would contradict; the later header simply contributes a better overload.
1081 */
1082 template<ConversionFactorType T>
1083 requires std::is_same_v<T, std::remove_cv_t<T>>
1084 struct strong
1085 {
1086 // UNQUALIFIED call so ADL on T* is performed: T is a conversion_factor whose associated namespaces are
1087 // `units` and (via its base detail::_conversion_factor) `units::detail`, so every dimension header's
1088 // strong_name registration in units::detail is found, along with the identity fallback. A qualified call
1089 // (::units::detail::strong_name) would SUPPRESS ADL and see only the fallback — the whole point is ADL.
1090 using type = decltype(strong_name(static_cast<T*>(nullptr)));
1091 };
1092
1093 template<class T>
1094 using strong_t = typename strong<T>::type;
1095 } // namespace traits
1097 //------------------------------
1098 // DIMENSIONS
1099 //------------------------------
1100 // see: https://github.com/swatanabe/cppnow17-units
1101 // license for this code: https://github.com/swatanabe/cppnow17-units/blob/master/LICENSE_1_0.txt
1102 //------------------------------
1104 template<class D, class E>
1105 struct dim
1107 using dimension = D;
1108 using exponent = E;
1109 };
1110
1111 template<class... D>
1113
1114 template<>
1115 struct dimension_t<>
1116 {
1117 static constexpr bool empty = true;
1118 };
1119
1120 template<class D0, class... D>
1121 struct dimension_t<D0, D...>
1122 {
1123 static constexpr bool empty = false;
1124 using front = D0;
1125 using pop_front = dimension_t<D...>;
1126 };
1127
1128 template<class T, class U>
1130
1131 template<int Test>
1132 struct merge_dimensions_impl;
1133
1134 constexpr int const_strcmp(const char* lhs, const char* rhs)
1135 {
1136 return (*lhs && *rhs) ? (*lhs == *rhs ? const_strcmp(lhs + 1, rhs + 1) : (*lhs < *rhs ? -1 : 1)) : ((!*lhs && !*rhs) ? 0 : (!*lhs ? -1 : 1));
1137 }
1138
1139 template<bool HasT, bool HasU>
1142 template<>
1143 struct merge_dimensions_recurse_impl<true, true>
1145 template<class T, class U, class... R>
1146 using apply = typename merge_dimensions_impl<const_strcmp(T::front::dimension::name, U::front::dimension::name)>::template apply<T, U, R...>;
1147 };
1148
1149 template<class T, class U>
1150 struct append;
1151
1152 template<class... T, class... U>
1153 struct append<dimension_t<T...>, dimension_t<U...>>
1154 {
1155 using type = dimension_t<T..., U...>;
1156 };
1158 template<>
1159 struct merge_dimensions_recurse_impl<true, false>
1160 {
1161 template<class T, class U, class... R>
1162 using apply = typename append<dimension_t<R...>, T>::type;
1163 };
1165 template<>
1166 struct merge_dimensions_recurse_impl<false, true>
1167 {
1168 template<class T, class U, class... R>
1169 using apply = typename append<dimension_t<R...>, U>::type;
1170 };
1171
1172 template<>
1173 struct merge_dimensions_recurse_impl<false, false>
1175 template<class T, class U, class... R>
1176 using apply = dimension_t<R...>;
1177 };
1178
1179 template<class T, class U, class... R>
1180 using merge_dimensions_recurse = typename merge_dimensions_recurse_impl<!T::empty, !U::empty>::template apply<T, U, R...>;
1182 template<>
1183 struct merge_dimensions_impl<1>
1184 {
1185 template<class T, class U, class... R>
1186 using apply = merge_dimensions_recurse<T, typename U::pop_front, R..., typename U::front>;
1187 };
1189 template<>
1190 struct merge_dimensions_impl<-1>
1192 template<class T, class U, class... R>
1193 using apply = merge_dimensions_recurse<typename T::pop_front, U, R..., typename T::front>;
1194 };
1195
1196 template<bool Cancels>
1199 template<>
1201 {
1202 template<class T, class U, class X, class... R>
1203 using apply = merge_dimensions_recurse<T, U, R...>;
1204 };
1206 template<>
1207 struct merge_dimensions_combine_impl<false>
1208 {
1209 template<class T, class U, class X, class... R>
1210 using apply = merge_dimensions_recurse<T, U, R..., X>;
1211 };
1212
1213 template<>
1214 struct merge_dimensions_impl<0>
1215 {
1216 template<class T, class U, class... R>
1217 using apply = typename merge_dimensions_combine_impl<std::ratio_add<typename T::front::exponent, typename U::front::exponent>::num == 0>::template apply<typename T::pop_front,
1220
1221 template<class T, class U>
1222 using merge_dimensions = merge_dimensions_recurse<T, U>;
1223
1224 template<class T, class E>
1225 struct dimension_pow_impl;
1226
1227 template<class... T, class... E, class R>
1228 struct dimension_pow_impl<dimension_t<dim<T, E>...>, R>
1229 {
1231 };
1232
1233 template<class T, class E>
1234 using dimension_pow = typename dimension_pow_impl<T, E>::type;
1235
1236 template<class T, class E>
1237 using dimension_root = dimension_pow<T, std::ratio_divide<std::ratio<1>, E>>;
1238
1239 template<class T, class U>
1240 using dimension_multiply = merge_dimensions<T, U>;
1241
1242 template<class T, class U>
1243 using dimension_divide = merge_dimensions<T, dimension_pow<U, std::ratio<-1>>>;
1244
1245 template<class T0 = void, class N0 = std::ratio<1>, class... Rest>
1246 struct make_dimension_list
1247 {
1248 using type = dimension_multiply<dimension_t<dim<T0, N0>>, typename make_dimension_list<Rest...>::type>;
1250
1251 template<class... T, class N0, class... Rest>
1252 struct make_dimension_list<dimension_t<T...>, N0, Rest...>
1253 {
1254 using type = dimension_multiply<dimension_pow<dimension_t<T...>, N0>, typename make_dimension_list<Rest...>::type>;
1255 };
1256
1257 template<>
1258 struct make_dimension_list<>
1259 {
1260 using type = dimension_t<>;
1261 };
1262
1263 template<class... T>
1264 using make_dimension = typename make_dimension_list<T...>::type;
1265
1266 //------------------------------
1267 // UNIT DIMENSIONS
1268 //------------------------------
1274
1275 namespace dimension
1276 {
1277 // DIMENSION TAGS
1278 struct length_tag
1279 {
1280 static constexpr auto name = "length";
1281 static constexpr auto abbreviation = "m";
1282 };
1283
1284 struct mass_tag
1285 {
1286 static constexpr auto name = "mass";
1287 static constexpr auto abbreviation = "kg";
1288 };
1289
1290 struct time_tag
1291 {
1292 static constexpr auto name = "time";
1293 static constexpr auto abbreviation = "s";
1294 };
1295
1296 struct current_tag
1297 {
1298 static constexpr auto name = "current";
1299 static constexpr auto abbreviation = "A";
1300 };
1301
1302 struct temperature_tag
1303 {
1304 static constexpr auto name = "temperature";
1305 static constexpr auto abbreviation = "K";
1306 };
1307
1308 struct substance_tag
1309 {
1310 static constexpr auto name = "amount of substance";
1311 static constexpr auto abbreviation = "mol";
1312 };
1313
1315 {
1316 static constexpr auto name = "luminous intensity";
1317 static constexpr auto abbreviation = "cd";
1318 };
1319
1320 struct angle_tag
1321 {
1322 static constexpr auto name = "angle";
1323 static constexpr auto abbreviation = "rad";
1324 };
1325
1326 struct data_tag
1327 {
1328 static constexpr auto name = "data";
1329 static constexpr auto abbreviation = "byte";
1330 };
1331
1332 // SI BASE UNITS
1333 using length = make_dimension<length_tag>;
1334 using mass = make_dimension<mass_tag>;
1335 using time = make_dimension<time_tag>;
1336 using current = make_dimension<current_tag>;
1337 using temperature = make_dimension<temperature_tag>;
1338 using substance = make_dimension<substance_tag>;
1339 using luminous_intensity = make_dimension<luminous_intensity_tag>;
1341 // dimensionless (DIMENSIONLESS) TYPES
1342 using dimensionless = dimension_t<>;
1343 using angle = make_dimension<angle_tag>;
1345 // SI DERIVED UNIT TYPES
1346 using solid_angle = dimension_pow<angle, std::ratio<2>>;
1347 using frequency = make_dimension<time, std::ratio<-1>>;
1348 using velocity = dimension_divide<length, time>;
1349 using angular_velocity = dimension_divide<angle, time>;
1350 using acceleration = dimension_divide<velocity, time>;
1351 using force = dimension_multiply<mass, acceleration>;
1352 using area = dimension_pow<length, std::ratio<2>>;
1353 using volume = dimension_pow<length, std::ratio<3>>;
1354 using volume_flow_rate = dimension_divide<volume, time>;
1355 using pressure = dimension_divide<force, area>;
1356 using charge = dimension_multiply<time, current>;
1357 using energy = dimension_multiply<force, length>;
1358 using power = dimension_divide<energy, time>;
1359 using voltage = dimension_divide<power, current>;
1360 using capacitance = dimension_divide<charge, voltage>;
1361 using impedance = dimension_divide<voltage, current>;
1362 using conductance = dimension_divide<current, voltage>;
1363 using magnetic_flux = dimension_divide<energy, current>;
1364 using inductance = dimension_multiply<impedance, time>;
1365 using luminous_flux = dimension_multiply<solid_angle, luminous_intensity>;
1366 using illuminance = make_dimension<luminous_flux, std::ratio<1>, length, std::ratio<-2>>;
1367 using luminance = make_dimension<luminous_intensity, std::ratio<1>, length, std::ratio<-2>>;
1368 using radioactivity = make_dimension<length, std::ratio<2>, time, std::ratio<-2>>;
1369 using substance_mass = dimension_divide<mass, substance>;
1370 using substance_concentration = dimension_divide<substance, mass>;
1371 using magnetic_field_strength = make_dimension<mass, std::ratio<1>, time, std::ratio<-2>, current, std::ratio<-1>>;
1372 using radiant_intensity = make_dimension<power, std::ratio<1>, solid_angle, std::ratio<-1>>;
1373 using radiance = make_dimension<radiant_intensity, std::ratio<1>, area, std::ratio<-1>>;
1374 using irradiance = make_dimension<power, std::ratio<1>, area, std::ratio<-1>>;
1375 using spectral_intensity = make_dimension<radiant_intensity, std::ratio<1>, length, std::ratio<-1>>;
1376 using spectral_flux = make_dimension<power, std::ratio<1>, length, std::ratio<-1>>;
1377 using spectral_radiance = make_dimension<radiant_intensity, std::ratio<1>, volume, std::ratio<-1>>;
1378 using spectral_irradiance = make_dimension<power, std::ratio<1>, volume, std::ratio<-1>>;
1380 // OTHER UNIT TYPES
1381 using jerk = make_dimension<length, std::ratio<1>, time, std::ratio<-3>>;
1382 using torque = dimension_multiply<force, length>;
1383 using density = dimension_divide<mass, volume>;
1384 using dynamic_viscosity = dimension_multiply<pressure, time>;
1385 using kinematic_viscosity = dimension_divide<area, time>;
1386 using energy_density = make_dimension<energy, std::ratio<1>, volume, std::ratio<-1>>;
1387 using concentration = make_dimension<volume, std::ratio<-1>>;
1388 using data = make_dimension<data_tag>;
1389 using data_transfer_rate = dimension_divide<data, time>;
1390 } // namespace dimension
1391
1392 //------------------------------
1393 // CONVERSION FACTOR CLASSES
1394 //------------------------------
1395 // DOXYGEN IGNORE
1400 template<RatioType, class, RatioType, RatioType>
1401 struct conversion_factor;
1402
1403 template<RatioType Conversion, class... Exponents, RatioType PiExponent, RatioType Translation>
1404 struct conversion_factor<Conversion, dimension_t<Exponents...>, PiExponent, Translation> : detail::_conversion_factor
1405 {
1406 using dimension_type = dimension_t<Exponents...>;
1407 using conversion_ratio = Conversion;
1408 using translation_ratio = Translation;
1409 using pi_exponent_ratio = PiExponent;
1410 };
1411 // END DOXYGEN IGNORE
1413 // DOXYGEN IGNORE
1415 namespace detail
1416 {
1417 template<RatioType C, typename U, RatioType P, RatioType T>
1418 conversion_factor<C, U, P, T> conversion_factor_base_t_impl(conversion_factor<C, U, P, T>* cf)
1419 {
1420 return *cf;
1421 };
1422
1423 template<typename T>
1424 using conversion_factor_base_t = decltype(conversion_factor_base_t_impl(std::declval<T*>()));
1425
1432 template<class ConversionFactor>
1433 struct dimension_of_impl : dimension_of_impl<conversion_factor_base_t<ConversionFactor>>
1434 {
1435 };
1436
1437 template<RatioType Conversion, class BaseUnit, RatioType PiExponent, RatioType Translation>
1438 struct dimension_of_impl<conversion_factor<Conversion, BaseUnit, PiExponent, Translation>> : dimension_of_impl<BaseUnit>
1439 {
1440 };
1441
1442 template<class... Exponents>
1443 struct dimension_of_impl<dimension_t<Exponents...>>
1444 {
1445 using type = dimension_t<Exponents...>;
1446 };
1447
1448 template<>
1449 struct dimension_of_impl<void>
1450 {
1451 using type = void;
1452 };
1453 } // namespace detail // END DOXYGEN IGNORE
1456 namespace traits
1457 {
1463 template<class U>
1464 using dimension_of_t = typename units::detail::dimension_of_impl<U>::type;
1465 } // namespace traits
1466
1467 template<ConversionFactorType ConversionFactor, ArithmeticType T, NumericalScaleType<T> NumericalScale>
1468 class unit;
1469 // DOXYGEN IGNORE
1471 namespace detail
1472 {
1473 template<typename T, class Dim, bool IsConv = false>
1474 struct has_dimension_of_impl : std::false_type
1475 {
1476 };
1477
1478 template<typename T, class Dim>
1479 using has_dimension_of = typename has_dimension_of_impl<T, Dim, traits::is_conversion_factor_v<T>>::type;
1480
1481 template<typename Cf, class Dim>
1482 struct has_dimension_of_impl<Cf, Dim, true> : has_dimension_of<conversion_factor_base_t<Cf>, Dim>::type
1483 {
1484 };
1485
1486 template<typename C, typename Cf, typename P, typename T, class Dim>
1487 struct has_dimension_of_impl<conversion_factor<C, Cf, P, T>, Dim, true> : std::is_same<typename conversion_factor<C, Cf, P, T>::dimension_type, Dim>::type
1488 {
1489 };
1490
1491 template<typename Cf, typename T, class Ns, class Dim>
1492 struct has_dimension_of_impl<unit<Cf, T, Ns>, Dim> : std::is_same<traits::dimension_of_t<Cf>, Dim>::type
1493 {
1494 };
1495 } // namespace detail // END DOXYGEN IGNORE
1497
1498 namespace traits
1499 {
1500 /**
1501 * @ingroup TypeTraits
1502 * @brief SFINAE-able trait which replaces the underlying type of `Unit` with `Underlying`.
1503 * @details If `Unit` is an unit, the member `type` alias names the same unit with an underlying type of
1504 * `Underlying`. Otherwise, there is no `type` member.
1505 * @param Unit The unit type whose underlying type is to be replaced.
1506 * @param Underlying The underlying type to replace that of `Unit`.
1507 */
1508 template<class, class>
1509 struct replace_underlying
1510 {
1511 };
1512
1513 template<ConversionFactorType Cf, ArithmeticType T, NumericalScaleType<T> Ns, ArithmeticType Underlying>
1514 struct replace_underlying<unit<Cf, T, Ns>, Underlying>
1516 using type = unit<Cf, Underlying, Ns>;
1517 };
1518
1519 template<class Unit, class Underlying>
1520 using replace_underlying_t = typename replace_underlying<Unit, Underlying>::type;
1521
1522 // True for dimensionless units whose conversion_ratio is not 1: percent, ppm, ppb, ppt, etc.
1523 template<class ConversionFactor, class = void>
1524 struct is_ratio_dimensionless_cf : std::false_type
1525 {
1526 };
1527
1528 template<class ConversionFactor>
1529 struct is_ratio_dimensionless_cf<ConversionFactor, std::void_t<typename ConversionFactor::dimension_type, typename ConversionFactor::conversion_ratio>>
1530 : std::bool_constant<std::is_same_v<typename ConversionFactor::dimension_type, dimension::dimensionless> && !std::ratio_equal_v<typename ConversionFactor::conversion_ratio, std::ratio<1>>>
1532 };
1533
1534 template<class ConversionFactor>
1535 inline constexpr bool is_ratio_dimensionless_cf_v = is_ratio_dimensionless_cf<ConversionFactor>::value;
1536
1537 } // namespace traits
1538
1539 template<typename U>
1540 concept RatioDimensionlessUnitType = units::DimensionlessUnitType<U> && traits::is_ratio_dimensionless_cf_v<typename U::conversion_factor>;
1541
1542 template<typename U>
1544
1562 * `struct meters : conversion_factor<std::ratio<1>, units::dimension::length> {};`,
1563 * or type alias, i.e. `using inches = conversion_factor<std::ratio<1,12>, feet>`.
1564 * @tparam Conversion std::ratio representing dimensionless multiplication factor.
1565 * @tparam BaseUnit Unit type which this unit is derived from. May be a `dimension_t`, or another
1566 * `conversion_factor`.
1567 * @tparam PiExponent std::ratio representing the exponent of pi required by the conversion.
1568 * @tparam Translation std::ratio representing any datum translation required by the conversion.
1569 */
1570 template<RatioType Conversion, class BaseUnit, RatioType PiExponent = std::ratio<0>, RatioType Translation = std::ratio<0>>
1571 struct conversion_factor : detail::_conversion_factor
1572 {
1573 using dimension_type = traits::dimension_of_t<BaseUnit>;
1574 using conversion_ratio = std::ratio_multiply<typename BaseUnit::conversion_ratio, Conversion>;
1575 using pi_exponent_ratio = std::ratio_add<typename BaseUnit::pi_exponent_ratio, PiExponent>;
1576 using translation_ratio = std::ratio_add<std::ratio_multiply<typename BaseUnit::conversion_ratio, Translation>, typename BaseUnit::translation_ratio>;
1577 };
1578
1579 //------------------------------
1580 // UNIT MANIPULATORS
1581 //------------------------------
1582 // DOXYGEN IGNORE
1584 namespace detail
1585 {
1592 template<ConversionFactorType Cf1, ConversionFactorType Cf2>
1593 struct unit_multiply_impl
1594 {
1596 dimension_multiply<traits::dimension_of_t<typename Cf1::dimension_type>, traits::dimension_of_t<typename Cf2::dimension_type>>,
1597 std::ratio_add<typename Cf1::pi_exponent_ratio, typename Cf2::pi_exponent_ratio>>;
1598 };
1599
1604 template<ConversionFactorType Cf1, ConversionFactorType Cf2>
1605 using unit_multiply = typename unit_multiply_impl<Cf1, Cf2>::type;
1606
1613 template<ConversionFactorType Cf1, ConversionFactorType Cf2>
1614 struct unit_divide_impl
1615 {
1616 using type = conversion_factor<std::ratio_divide<typename Cf1::conversion_ratio, typename Cf2::conversion_ratio>,
1617 dimension_divide<traits::dimension_of_t<typename Cf1::dimension_type>, traits::dimension_of_t<typename Cf2::dimension_type>>,
1618 std::ratio_subtract<typename Cf1::pi_exponent_ratio, typename Cf2::pi_exponent_ratio>>;
1619 };
1620
1625 template<ConversionFactorType Cf1, ConversionFactorType Cf2>
1626 using unit_divide = typename unit_divide_impl<Cf1, Cf2>::type;
1627
1634 template<ConversionFactorType Cf>
1635 struct inverse_impl
1636 {
1637 using type = conversion_factor<std::ratio<Cf::conversion_ratio::den, Cf::conversion_ratio::num>, dimension_pow<typename Cf::dimension_type, std::ratio<-1>>,
1638 std::ratio_multiply<typename Cf::pi_exponent_ratio, std::ratio<-1>>>; // inverses are rates or changes, so translation factor is removed.
1639 };
1640 } // namespace detail // END DOXYGEN IGNORE
1642
1649 template<ConversionFactorType Cf>
1650 using inverse = typename detail::inverse_impl<Cf>::type;
1651 // DOXYGEN IGNORE
1653 namespace detail
1654 {
1660 template<ConversionFactorType Cf>
1661 struct squared_impl
1662 {
1663 using Conversion = typename Cf::conversion_ratio;
1664 using type = conversion_factor<std::ratio_multiply<Conversion, Conversion>, dimension_pow<traits::dimension_of_t<typename Cf::dimension_type>, std::ratio<2>>,
1665 std::ratio_multiply<typename Cf::pi_exponent_ratio, std::ratio<2>>, typename Cf::translation_ratio>;
1666 };
1667 } // namespace detail // END DOXYGEN IGNORE
1669
1676 template<ConversionFactorType Cf>
1677 using squared = typename detail::squared_impl<Cf>::type;
1678 // DOXYGEN IGNORE
1680 namespace detail
1681 {
1687 template<ConversionFactorType Cf>
1688 struct cubed_impl
1689 {
1690 using Conversion = typename Cf::conversion_ratio;
1692 dimension_pow<traits::dimension_of_t<typename Cf::dimension_type>, std::ratio<3>>, std::ratio_multiply<typename Cf::pi_exponent_ratio, std::ratio<3>>, typename Cf::translation_ratio>;
1693 };
1694 } // namespace detail // END DOXYGEN IGNORE
1696
1703 template<ConversionFactorType Cf>
1704 using cubed = typename detail::cubed_impl<Cf>::type;
1705 // DOXYGEN IGNORE
1707 // clang-format off
1708 namespace detail
1709 {
1710 //----------------------------------
1711 // RATIO_SQRT IMPLEMENTATION
1712 //----------------------------------
1713
1714 using Zero = std::ratio<0>;
1715 using One = std::ratio<1>;
1716 template <RatioType R> using Square = std::ratio_multiply<R, R>;
1717
1718 // Find the largest std::integer N such that Predicate<N>::value is true.
1719 template <template <std::intmax_t N> class Predicate, typename = void>
1720 struct BinarySearch
1721 {
1722 template <std::intmax_t N>
1723 struct SafeDouble_
1724 {
1725 static constexpr const std::intmax_t value = 2 * N;
1726 static_assert(value > 0, "Overflows when computing 2 * N");
1727 };
1728
1729 template <std::intmax_t Lower, std::intmax_t Upper, typename Condition1 = void, typename Condition2 = void>
1730 struct DoubleSidedSearch_ : DoubleSidedSearch_<Lower, Upper,
1731 std::integral_constant<bool, (Upper - Lower == 1)>,
1732 std::integral_constant<bool, ((Upper - Lower>1 && Predicate<Lower + (Upper - Lower) / 2>::value))>> {};
1733
1734 template <std::intmax_t Lower, std::intmax_t Upper>
1735 struct DoubleSidedSearch_<Lower, Upper, std::false_type, std::false_type> : DoubleSidedSearch_<Lower, Lower + (Upper - Lower) / 2> {};
1736
1737 template <std::intmax_t Lower, std::intmax_t Upper, typename Condition2>
1738 struct DoubleSidedSearch_<Lower, Upper, std::true_type, Condition2> : std::integral_constant<std::intmax_t, Lower>{};
1739
1740 template <std::intmax_t Lower, std::intmax_t Upper, typename Condition1>
1741 struct DoubleSidedSearch_<Lower, Upper, Condition1, std::true_type> : DoubleSidedSearch_<Lower + (Upper - Lower) / 2, Upper>{};
1742
1743 template <std::intmax_t Lower, class = void>
1744 struct SingleSidedSearch_ : SingleSidedSearch_<Lower, std::integral_constant<bool, Predicate<SafeDouble_<Lower>::value>::value>>{};
1745
1746 template <std::intmax_t Lower>
1747 struct SingleSidedSearch_<Lower, std::false_type> : DoubleSidedSearch_<Lower, SafeDouble_<Lower>::value> {};
1748
1749 template <std::intmax_t Lower>
1750 struct SingleSidedSearch_<Lower, std::true_type> : SingleSidedSearch_<SafeDouble_<Lower>::value>{};
1751
1752 static constexpr std::intmax_t value = SingleSidedSearch_<1>::value;
1753 };
1754
1755 template <template <std::intmax_t N> class Predicate>
1756 struct BinarySearch<Predicate, std::enable_if_t<!Predicate<1>::value>> : std::integral_constant<std::intmax_t, 0>{};
1757
1758 // Find largest std::integer N such that N<=sqrt(R)
1759 template <typename R>
1760 struct Integer
1761 {
1762 template <std::intmax_t N> using Predicate_ = std::ratio_less_equal<std::ratio<N>, std::ratio_divide<R, std::ratio<N>>>;
1763 static constexpr const std::intmax_t value = BinarySearch<Predicate_>::value;
1764 };
1765
1766 template <typename R>
1767 struct IsPerfectSquare
1768 {
1769 static constexpr const std::intmax_t DenSqrt_ = Integer<std::ratio<R::den>>::value;
1770 static constexpr const std::intmax_t NumSqrt_ = Integer<std::ratio<R::num>>::value;
1771 static constexpr const bool value =( DenSqrt_ * DenSqrt_ == R::den && NumSqrt_ * NumSqrt_ == R::num);
1772 using Sqrt = std::ratio<NumSqrt_, DenSqrt_>;
1773 };
1774
1775 // Represents sqrt(P)-Q.
1776 template <typename Tp, typename Tq>
1777 struct Remainder
1778 {
1779 using P = Tp;
1780 using Q = Tq;
1781 };
1782
1783 // Represents 1/R = I + Rem where R is a Remainder.
1784 template <typename R>
1785 struct Reciprocal
1786 {
1787 using P_ = typename R::P;
1788 using Q_ = typename R::Q;
1789 using Den_ = std::ratio_subtract<P_, Square<Q_>>;
1790 using A_ = std::ratio_divide<Q_, Den_>;
1791 using B_ = std::ratio_divide<P_, Square<Den_>>;
1792 static constexpr const std::intmax_t I_ = (A_::num + Integer<std::ratio_multiply<B_, Square<std::ratio<A_::den>>>>::value) / A_::den;
1793 using I = std::ratio<I_>;
1794 using Rem = Remainder<B_, std::ratio_subtract<I, A_>>;
1795 };
1796
1797 // Expands sqrt(R) to continued fraction:
1798 // f(x)=C1+1/(C2+1/(C3+1/(...+1/(Cn+x)))) = (U*x+V)/(W*x+1) and sqrt(R)=f(Rem).
1799 // The error |f(Rem)-V| = |(U-W*V)x/(W*x+1)| <= |U-W*V|*Rem <= |U-W*V|/I' where
1800 // I' is the std::integer part of reciprocal of Rem.
1801 template <typename Tr, std::intmax_t N>
1802 struct ContinuedFraction
1803 {
1804 template <typename T>
1805 using Abs_ = std::conditional_t<std::ratio_less_v<T, Zero>, std::ratio_subtract<Zero, T>, T>;
1806
1807 using R = Tr;
1808 using Last_ = ContinuedFraction<R, N - 1>;
1809 using Reciprocal_ = Reciprocal<typename Last_::Rem>;
1810 using Rem = typename Reciprocal_::Rem;
1811 using I_ = typename Reciprocal_::I;
1812 using Den_ = std::ratio_add<typename Last_::W, I_>;
1813 using U = std::ratio_divide<typename Last_::V, Den_>;
1814 using V = std::ratio_divide<std::ratio_add<typename Last_::U, std::ratio_multiply<typename Last_::V, I_>>, Den_>;
1815 using W = std::ratio_divide<One, Den_>;
1816 using Error = Abs_<std::ratio_divide<std::ratio_subtract<U, std::ratio_multiply<V, W>>, typename Reciprocal<Rem>::I>>;
1817 };
1818
1819 template <typename Tr>
1820 struct ContinuedFraction<Tr, 1>
1821 {
1822 using R = Tr;
1823 using U = One;
1824 using V = std::ratio<Integer<R>::value>;
1825 using W = Zero;
1826 using Rem = Remainder<R, V>;
1827 using Error = std::ratio_divide<One, typename Reciprocal<Rem>::I>;
1828 };
1829
1830 template <typename R, typename Eps, std::intmax_t N = 1, typename = void>
1831 struct Sqrt_ : Sqrt_<R, Eps, N + 1> {};
1832
1833 template <typename R, typename Eps, std::intmax_t N>
1834 struct Sqrt_<R, Eps, N, std::enable_if_t<std::ratio_less_equal_v<typename ContinuedFraction<R, N>::Error, Eps>>>
1835 {
1836 using type = typename ContinuedFraction<R, N>::V;
1837 };
1838
1839 template <typename R, typename, typename = void>
1840 struct Sqrt
1841 {
1842 static_assert(std::ratio_greater_equal_v<R, Zero>, "R can't be negative");
1843 };
1844
1845 template <typename R, typename Eps>
1846 struct Sqrt<R, Eps, std::enable_if_t<std::ratio_greater_equal_v<R, Zero> && IsPerfectSquare<R>::value>>
1847 {
1848 using type = typename IsPerfectSquare<R>::Sqrt;
1849 };
1850
1851 template <typename R, typename Eps>
1852 struct Sqrt<R, Eps, std::enable_if_t<(std::ratio_greater_equal_v<R, Zero> && !IsPerfectSquare<R>::value)>> : Sqrt_<R, Eps>{};
1853 }
1854 // clang-format on // END DOXYGEN IGNORE
1856
1877 template<RatioType Ratio, std::intmax_t Eps = 10000000000>
1878 using ratio_sqrt = typename units::detail::Sqrt<Ratio, std::ratio<1, Eps>>::type;
1879 // DOXYGEN IGNORE
1881 namespace detail
1882 {
1888 template<ConversionFactorType Unit, std::intmax_t Eps>
1889 struct sqrt_impl
1890 {
1891 using Conversion = typename Unit::conversion_ratio;
1892 using type = conversion_factor<ratio_sqrt<Conversion, Eps>, dimension_root<traits::dimension_of_t<typename Unit::dimension_type>, std::ratio<2>>,
1893 std::ratio_divide<typename Unit::pi_exponent_ratio, std::ratio<2>>, typename Unit::translation_ratio>;
1894 };
1895 } // namespace detail // END DOXYGEN IGNORE
1897
1919 template<ConversionFactorType Cf, std::intmax_t Eps = 10000000000>
1920 using square_root = typename detail::sqrt_impl<Cf, Eps>::type;
1921
1922 //------------------------------
1923 // COMPOUND UNITS
1924 //------------------------------
1925 // DOXYGEN IGNORE
1927 namespace detail
1928 {
1934 template<ConversionFactorType Cf, ConversionFactorType... Cfs>
1935 struct compound_impl;
1936
1937 template<ConversionFactorType Cf>
1938 struct compound_impl<Cf>
1939 {
1940 using type = Cf;
1941 };
1942
1944 struct compound_impl<Cf1, Cf2, Cfs...> : compound_impl<unit_multiply<Cf1, Cf2>, Cfs...>
1945 {
1946 };
1947 } // namespace detail // END DOXYGEN IGNORE
1949
1961 template<ConversionFactorType Cf, ConversionFactorType... Cfs>
1962 using compound_conversion_factor = typename detail::compound_impl<Cf, Cfs...>::type;
1963
1964 //------------------------------
1965 // PREFIXES
1966 //------------------------------
1967 // DOXYGEN IGNORE
1969 namespace detail
1970 {
1975 template<RatioType Ratio, ConversionFactorType ConversionFactor>
1976 struct prefix
1977 {
1979 };
1980
1982 template<int N, RatioType R>
1983 struct power_of_ratio
1984 {
1985 using type = std::ratio_multiply<R, typename power_of_ratio<N - 1, R>::type>;
1986 };
1987
1989 template<RatioType R>
1990 struct power_of_ratio<1, R>
1991 {
1992 using type = R;
1993 };
1994 } // namespace detail // END DOXYGEN IGNORE
1997 // clang-format off
2002 template<ConversionFactorType Cf> using atto = typename detail::prefix<std::atto,Cf>::type;
2003 template<ConversionFactorType Cf> using femto = typename detail::prefix<std::femto,Cf>::type;
2004 template<ConversionFactorType Cf> using pico = typename detail::prefix<std::pico,Cf>::type;
2005 template<ConversionFactorType Cf> using nano = typename detail::prefix<std::nano,Cf>::type;
2006 template<ConversionFactorType Cf> using micro = typename detail::prefix<std::micro,Cf>::type;
2007 template<ConversionFactorType Cf> using milli = typename detail::prefix<std::milli,Cf>::type;
2008 template<ConversionFactorType Cf> using centi = typename detail::prefix<std::centi,Cf>::type;
2009 template<ConversionFactorType Cf> using deci = typename detail::prefix<std::deci,Cf>::type;
2010 template<ConversionFactorType Cf> using deca = typename detail::prefix<std::deca,Cf>::type;
2011 template<ConversionFactorType Cf> using hecto = typename detail::prefix<std::hecto,Cf>::type;
2012 template<ConversionFactorType Cf> using kilo = typename detail::prefix<std::kilo,Cf>::type;
2013 template<ConversionFactorType Cf> using mega = typename detail::prefix<std::mega,Cf>::type;
2014 template<ConversionFactorType Cf> using giga = typename detail::prefix<std::giga,Cf>::type;
2015 template<ConversionFactorType Cf> using tera = typename detail::prefix<std::tera,Cf>::type;
2016 template<ConversionFactorType Cf> using peta = typename detail::prefix<std::peta,Cf>::type;
2017 template<ConversionFactorType Cf> using exa = typename detail::prefix<std::exa, Cf>::type;
2024 template<ConversionFactorType Cf> using kibi = typename detail::prefix<std::ratio<1024>, Cf>::type;
2025 template<ConversionFactorType Cf> using mebi = typename detail::prefix<std::ratio<1048576>, Cf>::type;
2026 template<ConversionFactorType Cf> using gibi = typename detail::prefix<std::ratio<1073741824>, Cf>::type;
2027 template<ConversionFactorType Cf> using tebi = typename detail::prefix<std::ratio<1099511627776>, Cf>::type;
2028 template<ConversionFactorType Cf> using pebi = typename detail::prefix<std::ratio<1125899906842624>, Cf>::type;
2029 template<ConversionFactorType Cf> using exbi = typename detail::prefix<std::ratio<1152921504606846976>, Cf>::type;
2031 // clang-format on
2032
2033 //------------------------------
2034 // CONVERSION TRAITS
2035 //------------------------------
2036
2037 namespace traits
2038 {
2042 * are conversion factors to the same dimension.
2043 * @details The base characteristic is a specialization of the template `std::bool_constant`.
2044 * Use `is_same_dimension_conversion_factor_v<Cf1, Cf2>` to test whether `Cf1` and `Cf2`
2045 * are conversion factors to the same dimension.
2046 * @tparam Cf1 Conversion factor to query.
2047 * @tparam Cf2 Conversion factor to query.
2048 * @sa is_same_dimension_unit
2049 */
2050 template<ConversionFactorType Cf1, ConversionFactorType Cf2>
2052 : std::conjunction<std::is_same<dimension_of_t<typename conversion_factor_traits<Cf1>::dimension_type>, dimension_of_t<typename conversion_factor_traits<Cf2>::dimension_type>>>
2053 {
2054 };
2055
2056 template<ConversionFactorType Cf1, ConversionFactorType Cf2>
2057 inline constexpr bool is_same_dimension_conversion_factor_v = is_same_dimension_conversion_factor<Cf1, Cf2>::value;
2066 template<ConversionFactorType Cf>
2067 inline constexpr bool is_affine_conversion_factor_v = !std::ratio_equal_v<typename conversion_factor_traits<Cf>::translation_ratio, std::ratio<0>>;
2068 } // namespace traits
2069
2070 //------------------------------
2071 // CONSTEXPR MATH FUNCTIONS
2072 //------------------------------
2073 // DOXYGEN IGNORE
2075 namespace detail
2076 {
2083 template<typename T>
2084 struct floating_point_promotion : std::conditional<std::is_floating_point_v<T>, T, double>
2085 {
2086 };
2087
2088 template<typename T>
2089 using floating_point_promotion_t = typename floating_point_promotion<T>::type;
2090
2091 template<ConversionFactorType Cf, typename T, class Ns>
2092 struct floating_point_promotion<unit<Cf, T, Ns>>
2093 {
2094 using type = unit<Cf, floating_point_promotion_t<T>, Ns>;
2095 };
2096
2108 template<class To, class From>
2109 constexpr To exact_integral_cast(From value)
2110 {
2111 const To result = static_cast<To>(value);
2112 if (static_cast<From>(result) != value)
2113 throw "a floating-point unit converts to an integral unit only when its value is a whole number in range";
2114 return result;
2115 }
2116 } // namespace detail
2117
2118 namespace Detail
2119 {
2120 template<std::floating_point T>
2121 constexpr T sqrtNewtonRaphson(T x, T curr, T prev)
2122 {
2123 return curr == prev ? curr : sqrtNewtonRaphson(x, T{0.5} * (curr + x / curr), curr);
2124 }
2125 } // namespace Detail // END DOXYGEN IGNORE
2127
2128 template<ArithmeticType T>
2129 constexpr detail::floating_point_promotion_t<T> sqrt(T x_)
2130 {
2131 using FloatingPoint = detail::floating_point_promotion_t<T>;
2132
2133 const FloatingPoint x(x_);
2134
2135 return x >= 0 && x < std::numeric_limits<FloatingPoint>::infinity() ? Detail::sqrtNewtonRaphson(x, x, FloatingPoint(0)) : std::numeric_limits<FloatingPoint>::quiet_NaN();
2136 }
2137 // DOXYGEN IGNORE
2139 namespace detail
2140 {
2141 template<unsigned long long Exp, typename B>
2142 constexpr auto pow_acc(B acc, B base [[maybe_unused]]) noexcept
2143 {
2144 if constexpr (Exp == 0)
2145 {
2146 return static_cast<B>(acc);
2147 }
2148 else if constexpr ((Exp & 1) == 0)
2149 {
2150 return pow_acc<Exp / 2>(acc, base * base);
2151 }
2152 else
2153 {
2154 return pow_acc<(Exp - 1) / 2>(acc * base, base * base);
2155 }
2156 }
2157 } // namespace detail // END DOXYGEN IGNORE
2159
2160 template<signed long long Exp, ArithmeticType B>
2161 constexpr detail::floating_point_promotion_t<B> pow(B base) noexcept
2162 {
2163 using promoted_t = detail::floating_point_promotion_t<B>;
2164 constexpr auto one = static_cast<promoted_t>(1);
2165 if constexpr (Exp >= 0)
2166 {
2167 return detail::pow_acc<Exp>(one, static_cast<promoted_t>(base));
2168 }
2169 constexpr auto new_exp = static_cast<unsigned long long>(-(Exp + 1));
2170 return 1 / (base * detail::pow_acc<new_exp>(one, static_cast<promoted_t>(base)));
2171 }
2172 // DOXYGEN IGNORE
2174 namespace detail
2175 {
2176 template<typename T1, typename T2>
2177 constexpr auto pow_acc(T1 acc, T1 x, T2 y) noexcept
2178 {
2179 if (y == 0)
2180 {
2181 return acc;
2182 }
2183 if (y % 2 == 0)
2184 {
2185 return pow_acc(acc, x * x, y / 2);
2186 }
2187 return pow_acc(acc * x, x * x, (y - 1) / 2);
2188 }
2189 } // namespace detail // END DOXYGEN IGNORE
2191
2192 template<ArithmeticType T1, ArithmeticType T2>
2193 requires std::is_unsigned_v<T2>
2194 constexpr detail::floating_point_promotion_t<T1> pow(T1 x, T2 y) noexcept
2195 {
2196 using promoted_t = detail::floating_point_promotion_t<T1>;
2197 return detail::pow_acc(static_cast<promoted_t>(1.0), static_cast<promoted_t>(x), y);
2198 }
2199
2200 template<ArithmeticType T1, ArithmeticType T2>
2201 requires std::is_signed_v<T2>
2202 constexpr detail::floating_point_promotion_t<T1> pow(T1 x, T2 y) noexcept
2203 {
2204 if (y >= 0)
2205 {
2206 return pow(x, static_cast<unsigned long long>(y));
2207 }
2208 return 1 / (x * pow(x, static_cast<unsigned long long>(-(y + 1))));
2209 }
2210
2211 template<ArithmeticType T>
2212 constexpr T abs(T x)
2213 {
2214 return x < 0 ? -x : x;
2215 }
2217 //------------------------------
2218 // CONVERSION FUNCTIONS
2219 //------------------------------
2220
2225 struct linearized_value_t
2226 {
2227 explicit linearized_value_t() = default;
2228 };
2229
2230 inline constexpr linearized_value_t linearized_value{};
2231 // DOXYGEN IGNORE
2233 namespace detail
2234 {
2240#if defined(__SIZEOF_INT128__)
2241 using widest_signed_int = __int128;
2242 using widest_unsigned_int = unsigned __int128;
2243 inline constexpr bool has_builtin_int128 = true;
2244#else
2245 using widest_signed_int = std::intmax_t;
2246 using widest_unsigned_int = std::uintmax_t;
2247 inline constexpr bool has_builtin_int128 = false;
2248#endif
2249
2254 template<class Rep>
2255 constexpr Rep widening_mul_div(Rep value, std::intmax_t num, std::intmax_t den) noexcept
2256 {
2257 if constexpr (has_builtin_int128)
2258 {
2259 return static_cast<Rep>(static_cast<widest_signed_int>(value) * static_cast<widest_signed_int>(num) / static_cast<widest_signed_int>(den));
2260 }
2261 else
2262 {
2263 // Sign-separated 64x64->128 multiply, then 128/64 divide, all in unsigned 64-bit limbs so no
2264 // intermediate exceeds the representable range. `num`/`den` are positive (a std::ratio is stored in
2265 // lowest terms with a positive denominator); only `value` may be negative.
2266 const bool negative = (value < 0);
2267 const std::uint64_t a = negative ? static_cast<std::uint64_t>(-(value + 1)) + 1u : static_cast<std::uint64_t>(value);
2268 const std::uint64_t b = static_cast<std::uint64_t>(num);
2269 const std::uint64_t d = static_cast<std::uint64_t>(den);
2270
2271 // 64x64 -> 128 as two 64-bit limbs (hi, lo).
2272 const std::uint64_t aLo = a & 0xFFFFFFFFull, aHi = a >> 32;
2273 const std::uint64_t bLo = b & 0xFFFFFFFFull, bHi = b >> 32;
2274 const std::uint64_t ll = aLo * bLo;
2275 const std::uint64_t lh = aLo * bHi;
2276 const std::uint64_t hl = aHi * bLo;
2277 const std::uint64_t hh = aHi * bHi;
2278 const std::uint64_t cross = (ll >> 32) + (lh & 0xFFFFFFFFull) + (hl & 0xFFFFFFFFull);
2279 std::uint64_t hi = hh + (lh >> 32) + (hl >> 32) + (cross >> 32);
2280 std::uint64_t lo = (cross << 32) | (ll & 0xFFFFFFFFull);
2281
2282 // 128 (hi:lo) / d -> long division of the two limbs by a 64-bit divisor.
2283 std::uint64_t quotient = 0;
2284 std::uint64_t rem = 0;
2285 for (int bit = 127; bit >= 0; --bit)
2286 {
2287 rem = (rem << 1) | ((bit >= 64 ? (hi >> (bit - 64)) : (lo >> bit)) & 1u);
2288 const bool canSubtract = (rem >= d);
2289 rem -= canSubtract ? d : 0u;
2290 if (bit < 64)
2291 quotient |= (static_cast<std::uint64_t>(canSubtract) << bit);
2292 }
2293 const auto result = static_cast<Rep>(quotient);
2294 return negative ? static_cast<Rep>(-result) : result;
2295 }
2296 }
2297
2304 template<class Rep>
2305 constexpr bool integral_conversion_is_exact(Rep value, std::intmax_t num, std::intmax_t den) noexcept
2306 {
2307 const widest_signed_int product = static_cast<widest_signed_int>(value) * static_cast<widest_signed_int>(num);
2308 return product % static_cast<widest_signed_int>(den) == 0;
2309 }
2310
2323 template<class To, class From>
2324 constexpr To exact_integral_unit_cast(From value, std::intmax_t num, std::intmax_t den)
2325 {
2326 if (!integral_conversion_is_exact(value, num, den))
2327 throw "an integral unit converts to a coarser integral unit only when the value is an exact whole number of the target unit";
2328 return static_cast<To>(widening_mul_div(value, num, den));
2329 }
2330 } // namespace detail // END DOXYGEN IGNORE
2332
2345 * @tparam From type of <i>value</i>. Shall be an arithmetic type.
2346 * @param[in] value Arithmetic value to convert.
2347 * The value should represent a quantity in units of `ConversionFactorFrom`.
2348 * @tparam To type of the converted unit value. Shall be an arithmetic type.
2349 * @returns value, converted from units of `ConversionFactorFrom` to `ConversionFactorTo`.
2350 * The value represents a quantity in units of `ConversionFactorTo`.
2351 */
2352 template<ConversionFactorType ConversionFactorFrom, ConversionFactorType ConversionFactorTo, ArithmeticType To = UNIT_LIB_DEFAULT_TYPE, ArithmeticType From>
2353 requires(traits::is_same_dimension_conversion_factor_v<ConversionFactorFrom, ConversionFactorTo>)
2354 constexpr To convert(const From& value) noexcept
2355 {
2356 using Ratio = std::ratio_divide<typename ConversionFactorFrom::conversion_ratio, typename ConversionFactorTo::conversion_ratio>;
2357 using PiRatio = std::ratio_subtract<typename ConversionFactorFrom::pi_exponent_ratio, typename ConversionFactorTo::pi_exponent_ratio>;
2358 using Translation =
2359 std::ratio_divide<std::ratio_subtract<typename ConversionFactorFrom::translation_ratio, typename ConversionFactorTo::translation_ratio>, typename ConversionFactorTo::conversion_ratio>;
2360
2361 [[maybe_unused]] constexpr auto normal_convert = []<typename T0>(const T0& val)
2362 {
2366 };
2367
2368 [[maybe_unused]] constexpr auto pi_convert = []<typename T0>(const T0& val)
2369 {
2370 using ResolvedUnitFrom =
2374 };
2375
2376 // same exact unit on both sides
2377 if constexpr (std::same_as<ConversionFactorFrom, ConversionFactorTo>)
2378 {
2379 return static_cast<To>(value);
2380 }
2381 // PI REQUIRED, no translation
2382 else if constexpr (!std::same_as<std::ratio<0>, PiRatio> && std::same_as<std::ratio<0>, Translation>)
2383 {
2384 using CommonUnderlying = std::common_type_t<To, From, UNIT_LIB_DEFAULT_TYPE>;
2385 // The pi exponent as a real number. Compute in long double: PiRatio::num/PiRatio::den are
2386 // intmax_t, so an integer division here would truncate a fractional exponent (e.g. ratio<1,2>
2387 // -> 0), which both corrupts the value and, for the fractional case, produced a non-constant
2388 // expression / missing-return compile error.
2389 constexpr long double PiRatioValue = static_cast<long double>(PiRatio::num) / static_cast<long double>(PiRatio::den);
2390 constexpr bool integerExponent = (PiRatio::num % PiRatio::den == 0);
2391
2392 // A whole-number exponent uses the constexpr integer `pow`; a fractional exponent needs
2393 // `std::pow` (not constant-evaluable), so that sole case degrades to a run-time computation.
2394 if constexpr (integerExponent && PiRatioValue >= 0)
2395 {
2396 return static_cast<To>(normal_convert(static_cast<CommonUnderlying>(value) * static_cast<CommonUnderlying>(pow(detail::PI_VAL, PiRatioValue))));
2397 }
2398 else if constexpr (integerExponent) // PiRatioValue < 0
2399 {
2400 return static_cast<To>(normal_convert(static_cast<CommonUnderlying>(value) / static_cast<CommonUnderlying>(pow(detail::PI_VAL, -PiRatioValue))));
2401 }
2402 else // fractional exponent (either sign): std::pow handles both directions
2403 {
2404 return static_cast<To>(normal_convert(static_cast<CommonUnderlying>(value) * static_cast<CommonUnderlying>(std::pow(detail::PI_VAL, PiRatioValue))));
2405 }
2406 }
2407 // Translation required, no pi variable
2408 else if constexpr (std::same_as<std::ratio<0>, PiRatio> && !std::same_as<std::ratio<0>, Translation>)
2409 {
2410 using CommonUnderlying = std::common_type_t<To, From, UNIT_LIB_DEFAULT_TYPE>;
2411
2412 return static_cast<To>(normal_convert(static_cast<CommonUnderlying>(value)) + (static_cast<CommonUnderlying>(Translation::num) / static_cast<CommonUnderlying>(Translation::den)));
2413 }
2414 // pi and translation needed
2415 else if constexpr (!std::same_as<std::ratio<0>, PiRatio> && !std::same_as<std::ratio<0>, Translation>)
2416 {
2417 using CommonUnderlying = std::common_type_t<To, From, UNIT_LIB_DEFAULT_TYPE>;
2418
2419 return static_cast<To>(pi_convert(static_cast<CommonUnderlying>(value)) + (static_cast<CommonUnderlying>(Translation::num) / static_cast<CommonUnderlying>(Translation::den)));
2420 }
2421 // normal conversion between two different units
2422 else
2423 {
2424 using CommonUnderlying = std::common_type_t<To, From, std::intmax_t>;
2425
2426 if constexpr (Ratio::num == 1 && Ratio::den == 1)
2427 return static_cast<To>(value);
2428 if constexpr (Ratio::num != 1 && Ratio::den == 1)
2429 return static_cast<To>(static_cast<CommonUnderlying>(value) * static_cast<CommonUnderlying>(Ratio::num));
2430 if constexpr (Ratio::num == 1 && Ratio::den != 1)
2431 return static_cast<To>(static_cast<CommonUnderlying>(value) / static_cast<CommonUnderlying>(Ratio::den));
2432 if constexpr (Ratio::num != 1 && Ratio::den != 1)
2433 {
2434 // A mul-then-divide conversion. The goal is the MOST accurate representable result:
2435 // - Integral intermediate: carry `value * num` in a double-width integer so it cannot overflow
2436 // before `/ den` recovers a value that fits the target (no wrong answer, no precision lost).
2437 // - Floating-point: `(value * num) / den` is the most accurate order (a single rounding) and is
2438 // used whenever `value * num` is representable. Only when that product would overflow to
2439 // infinity — a blatantly wrong answer where a finite result exists — fall back to the
2440 // divide-first order `value / den * num`, which trades a little rounding for a representable
2441 // answer. Normal-magnitude conversions therefore keep the correctly-rounded mul-then-divide.
2442 if constexpr (std::is_integral_v<CommonUnderlying>)
2443 {
2444 return static_cast<To>(detail::widening_mul_div(static_cast<CommonUnderlying>(value), Ratio::num, Ratio::den));
2445 }
2446 else
2447 {
2448 const CommonUnderlying v = static_cast<CommonUnderlying>(value);
2449 const CommonUnderlying num = static_cast<CommonUnderlying>(Ratio::num);
2450 const CommonUnderlying den = static_cast<CommonUnderlying>(Ratio::den);
2451 // `value * num` overflows the type when |value| exceeds max / num. Guard on that exact threshold
2452 // so the lossy divide-first path is taken ONLY when the accurate path would produce infinity.
2453 const CommonUnderlying limit = (std::numeric_limits<CommonUnderlying>::max)() / num;
2454 if (v > limit || v < -limit)
2455 return static_cast<To>((v / den) * num);
2456 return static_cast<To>((v * num) / den);
2457 }
2458 }
2459 }
2460 }
2461 // DOXYGEN IGNORE
2463 namespace detail
2464 {
2470 template<UnitType UnitFrom, UnitType UnitTo>
2471 struct delayed_is_same_dimension_conversion_factor : std::false_type
2472 {
2473 static constexpr bool value = traits::is_same_dimension_conversion_factor_v<typename UnitFrom::conversion_factor, typename UnitTo::conversion_factor>;
2474 };
2475 } // namespace detail // END DOXYGEN IGNORE
2477
2483 * computations are carried in the widest representation before being converted to `UnitTo`.
2484 * `is_same_dimension_unit_v<UnitFrom, UnitTo>` shall be `true`.
2485 * @sa unit for implicit conversion of unit containers.
2486 * @tparam UnitFrom unit to convert to `UnitTo`. `is_unit_v<UnitFrom>` shall be `true`.
2487 * @tparam UnitTo unit to convert `from` to. `is_unit_v<UnitTo>` shall be `true`.
2488 * @returns from, converted from units of `UnitFrom` to `UnitTo`.
2489 */
2490 template<UnitType UnitTo, UnitType UnitFrom>
2492 constexpr UnitTo convert(const UnitFrom& from) noexcept
2493 {
2495 }
2496
2497 //------------------------------
2498 // UNIT TYPE TRAITS
2499 //------------------------------
2500
2501 namespace traits
2502 {
2503#ifdef FOR_DOXYGEN_PURPOSOES_ONLY
2510 template<typename T>
2511 struct unit_traits
2512 {
2513 typedef typename T::numerical_scale_type numerical_scale_type;
2516 typedef typename T::underlying_type underlying_type;
2517 typedef typename T::value_type value_type;
2518 typedef typename T::conversion_factor conversion_factor;
2519 };
2520#endif
2521 // DOXYGEN IGNORE
2527 template<typename, typename = void>
2528 struct unit_traits
2529 {
2530 using numerical_scale_type = void;
2531 using underlying_type = void;
2532 using value_type = void;
2533 using conversion_factor = void;
2534 };
2535
2536 template<ArithmeticType T>
2537 struct unit_traits<T, std::void_t<T>>
2538 {
2539 using numerical_scale_type = void;
2540 using underlying_type = T;
2541 using value_type = void;
2542 using conversion_factor = units::conversion_factor<std::ratio<1>, dimension_t<>>;
2543 };
2544
2550 template<NonArithmeticType T>
2551 struct unit_traits<T, std::void_t<typename T::numerical_scale_type, typename T::underlying_type, typename T::value_type, typename T::conversion_factor>>
2552 {
2553 using numerical_scale_type = typename T::numerical_scale_type;
2554 using underlying_type = typename T::underlying_type;
2555 using value_type = typename T::value_type;
2556 using conversion_factor = typename T::conversion_factor;
2557 };
2558 // END DOXYGEN IGNORE
2560 } // namespace traits
2561
2562 namespace traits
2563 {
2567 template<UnitType U>
2568 inline constexpr bool is_affine_unit_v = is_affine_conversion_factor_v<typename unit_traits<U>::conversion_factor>;
2569
2572 * @brief `BinaryTypeTrait` for querying whether `U1` and `U2` are units of the same dimension.
2573 * @details The base characteristic is a specialization of the template `std::bool_constant`.
2574 * Use `is_same_dimension_unit_v<U1, U2>` to test whether `U1` and `U2`
2575 * are units of the same dimension.
2576 * @tparam U1 Unit to query.
2577 * @tparam U2 Unit to query.
2578 * @sa is_same_dimension_conversion_factor
2579 */
2580 template<UnitType U1, UnitType U2>
2582 : std::conjunction<is_unit<U1>, is_unit<U2>, is_same_dimension_conversion_factor<typename unit_traits<U1>::conversion_factor, typename unit_traits<U2>::conversion_factor>>
2583 {
2584 };
2585
2586 template<UnitType U1, UnitType U2>
2587 inline constexpr bool is_same_dimension_unit_v = is_same_dimension_unit<U1, U2>::value;
2588 } // namespace traits
2589
2590 //----------------------------------
2591 // UNIT TYPE
2592 //----------------------------------
2593 // DOXYGEN IGNORE
2595
2596 namespace detail
2597 {
2598 // Forward declaration so unit's name()/abbreviation() members (defined below, in the unit class) can name
2599 // detail::rewrap_to_named_t; the full definition follows after the unit class is complete (it depends on it).
2600 template<class U, class = void>
2601 struct rewrap_to_named;
2602 template<class U>
2603 using rewrap_to_named_t = typename rewrap_to_named<U>::type;
2604
2608 template<class From, class To>
2609 inline constexpr bool is_losslessly_convertible = std::is_arithmetic_v<From> && (std::is_floating_point_v<To> || !std::is_floating_point_v<From>);
2610
2615 template<ConversionFactorType ConversionFactorFrom, ConversionFactorType ConversionFactorTo>
2616 struct is_non_truncated_convertible_unit : std::false_type
2617 {
2618 static constexpr bool value = std::ratio_divide<typename ConversionFactorFrom::conversion_ratio, typename ConversionFactorTo::conversion_ratio>::den == 1;
2619 };
2620
2624 template<class UnitFrom, class UnitTo>
2625 inline constexpr bool is_losslessly_convertible_unit = std::conjunction_v<traits::is_same_dimension_unit<UnitFrom, UnitTo>,
2626 std::disjunction<std::is_floating_point<typename UnitTo::underlying_type>,
2627 std::conjunction<std::negation<std::is_floating_point<typename UnitFrom::underlying_type>>,
2628 is_non_truncated_convertible_unit<typename UnitFrom::conversion_factor, typename UnitTo::conversion_factor>>>>;
2629
2631 template<class L, class R>
2632 inline constexpr bool both_floating_v = std::is_floating_point_v<typename traits::unit_traits<L>::underlying_type> &&
2633 std::is_floating_point_v<typename traits::unit_traits<R>::underlying_type>;
2634
2641 template<class L, class R>
2642 using lhs_result_unit_t = std::conditional_t<is_losslessly_convertible_unit<R, L> || both_floating_v<L, R>, L, std::common_type_t<L, R>>;
2643
2644 // The underlying type a NAMED unit's from-unit deduction guide should produce when constructed from `Source`:
2645 // the source's own underlying when losslessly convertible into the target (StrongCf, Scale), else its
2646 // floating-point promotion (so e.g. radians(degrees{1}) deduces radians<double>). A SFINAE-friendly class
2647 // template (NOT a var-template init), so the guide's return type never eagerly instantiates
2648 // is_losslessly_convertible_unit for a non-unit / non-same-dimension Source — the primary is chosen and the
2649 // heavy check only runs in the partial specialization, which is constrained to a same-dimension unit source.
2650 template<class Source, class StrongCf, class Scale, class = void>
2651 struct deduced_named_underlying
2652 {
2653 using type = typename traits::unit_traits<Source>::underlying_type;
2654 };
2655 template<class Source, class StrongCf, class Scale>
2656 struct deduced_named_underlying<Source, StrongCf, Scale,
2657 std::enable_if_t<traits::is_unit_v<Source> &&
2658 traits::is_same_dimension_unit_v<Source, unit<StrongCf, typename traits::unit_traits<Source>::underlying_type, Scale>>>>
2659 {
2660 private:
2661 using Src = typename traits::unit_traits<Source>::underlying_type;
2662
2663 public:
2664 using type = std::conditional_t<is_losslessly_convertible_unit<Source, unit<StrongCf, Src, Scale>>, Src, floating_point_promotion_t<Src>>;
2665 };
2666 template<class Source, class StrongCf, class Scale>
2667 using deduced_named_underlying_t = typename deduced_named_underlying<Source, StrongCf, Scale>::type;
2668
2669 template<RatioType Ratio>
2670 using time_conversion_factor = conversion_factor<Ratio, dimension::time>;
2671
2675 template<ConversionFactorType ConversionFactor>
2676 inline constexpr bool is_time_conversion_factor = traits::is_same_dimension_conversion_factor_v<ConversionFactor, time_conversion_factor<std::ratio<1>>>;
2677 } // namespace detail // END DOXYGEN IGNORE
2679
2735#ifdef _WIN32
2736 // Microsoft compiler requires explicit activation of empty base class optimization
2737 // so that sizeof(unit<..., double, ...>) == sizeof(double)
2738#define MSVC_EBO __declspec(empty_bases)
2739#else
2740#define MSVC_EBO
2741#endif
2742 template<ConversionFactorType ConversionFactor, ArithmeticType T = UNIT_LIB_DEFAULT_TYPE, NumericalScaleType<T> NumericalScale = linear_scale>
2743 class MSVC_EBO unit : public ConversionFactor, public NumericalScale, public detail::_unit
2744 {
2745 public:
2746 using numerical_scale_type = NumericalScale;
2747 using underlying_type = T;
2748 using value_type = T;
2749 using conversion_factor = ConversionFactor;
2750
2755 constexpr unit() = default;
2756
2761 constexpr unit(const unit&) = default;
2762
2765
2768 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2769 requires traits::is_same_dimension_unit_v<unit<ConversionFactorRhs, Ty, NsRhs>, unit> && detail::is_losslessly_convertible_unit<unit<ConversionFactorRhs, Ty, NsRhs>, unit>
2770 constexpr unit(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) noexcept
2772 {
2773 }
2774
2782 * run-time floating-to-integral unit conversion remains rejected. Wholeness is judged on the
2783 * stored point count (`raw()`), so a ratio-dimensionless unit converts correctly too
2784 * (`percent<int> p = 1_pct;` is percent<int> holding 1, not a rejected 0.01).
2785 * @param[in] rhs unit to convert.
2786 */
2787 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2788 requires(traits::is_same_dimension_unit_v<unit<ConversionFactorRhs, Ty, NsRhs>, unit> &&
2789 !detail::is_losslessly_convertible_unit<unit<ConversionFactorRhs, Ty, NsRhs>, unit> &&
2790 std::is_floating_point_v<Ty> && std::is_integral_v<T>)
2791 consteval unit(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs)
2792 : _linearized_value(detail::exact_integral_cast<T>(unit<ConversionFactor, detail::floating_point_promotion_t<T>, NumericalScale>(rhs).raw()))
2793 {
2794 }
2795
2805 * arithmetic in a double-width intermediate, so it cannot be defeated by an intermediate overflow.
2806 * A run-time integral-to-coarser-integral unit conversion remains rejected; use `round`/`floor`/
2807 * `ceil`/`trunc<To>` for a deliberate run-time rounding.
2808 * @param[in] rhs unit to convert.
2809 */
2810 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2811 requires(traits::is_same_dimension_unit_v<unit<ConversionFactorRhs, Ty, NsRhs>, unit> &&
2812 !detail::is_losslessly_convertible_unit<unit<ConversionFactorRhs, Ty, NsRhs>, unit> &&
2813 std::is_integral_v<Ty> && std::is_integral_v<T>)
2814 consteval unit(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs)
2815 : _linearized_value(detail::exact_integral_unit_cast<T>(rhs.raw(),
2816 std::ratio_divide<typename ConversionFactorRhs::conversion_ratio, typename ConversionFactor::conversion_ratio>::num,
2817 std::ratio_divide<typename ConversionFactorRhs::conversion_ratio, typename ConversionFactor::conversion_ratio>::den))
2818 {
2820
2823
2826 template<ArithmeticType Ty>
2827 requires(!traits::is_dimensionless_unit<ConversionFactor>::value && detail::is_losslessly_convertible<Ty, T>)
2828 explicit constexpr unit(Ty value) noexcept
2829 : _linearized_value(NumericalScale::linearize(static_cast<T>(value)))
2830 {
2832
2835
2838 template<ArithmeticType Ty>
2839 requires detail::is_losslessly_convertible<Ty, T>
2840 explicit constexpr unit(Ty value, linearized_value_t) noexcept
2841 : _linearized_value(value)
2842 {
2844
2847
2850 template<ArithmeticType Ty>
2851 requires traits::is_dimensionless_unit<ConversionFactor>::value && detail::is_losslessly_convertible<Ty, T>
2852 constexpr unit(Ty value) noexcept
2853 : _linearized_value(NumericalScale::linearize(static_cast<T>(value)))
2854 {
2855 }
2860
2862 template<ArithmeticType Rep, RatioType Period>
2863 requires detail::is_time_conversion_factor<ConversionFactor> && detail::is_losslessly_convertible<Rep, T> &&
2864 detail::is_losslessly_convertible_unit<units::unit<units::conversion_factor<Period, dimension::time>, Rep>, unit>
2865 constexpr unit(const std::chrono::duration<Rep, Period>& value) noexcept
2867 {
2868 }
2869
2875 constexpr unit& operator=(const unit& rhs) noexcept = default;
2876
2881
2882 template<ConversionFactorType Cf = ConversionFactor>
2884 constexpr unit& operator=(const underlying_type& rhs) noexcept
2885 {
2886 unit<units::conversion_factor<std::ratio<1>, units::dimension::dimensionless>, underlying_type, linear_scale> dimensionlessRhs(rhs);
2887 _linearized_value = units::convert<unit>(dimensionlessRhs)._linearized_value;
2888 return *this;
2890
2893
2897 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2898 constexpr bool operator<(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2899 {
2900 return value_compare(rhs) < 0;
2902
2905
2909 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2910 constexpr bool operator<=(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2911 {
2912 return value_compare(rhs) <= 0;
2914
2917
2921 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2922 constexpr bool operator>(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2923 {
2924 return value_compare(rhs) > 0;
2926
2929
2933 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2934 constexpr bool operator>=(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2935 {
2936 return value_compare(rhs) >= 0;
2937 }
2938
2939 /**
2940 * @brief equality
2941 * @details compares the linearized value of two units. Performs unit conversions if necessary.
2942 * @param[in] rhs right-hand side unit for the comparison
2943 * @returns true IFF the value of `this` exactly equal to the value of rhs.
2944 * @note This may not be suitable for all applications when the underlying_type of unit is a double.
2945 */
2946 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2947 requires(std::floating_point<T> || std::floating_point<Ty>)
2948 constexpr bool operator==(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2949 {
2950 using CommonUnit = std::common_type_t<unit, unit<ConversionFactorRhs, Ty, NsRhs>>;
2951 using CommonUnderlying = typename CommonUnit::underlying_type;
2952
2953 const auto common_lhs(CommonUnit(*this)._linearized_value);
2954 const auto common_rhs(CommonUnit(rhs)._linearized_value);
2955
2956 return abs(common_lhs - common_rhs) < std::numeric_limits<CommonUnderlying>::epsilon() * abs(common_lhs + common_rhs) ||
2957 abs(common_lhs - common_rhs) < std::numeric_limits<CommonUnderlying>::min();
2958 }
2959
2960 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2961 requires(std::integral<T> && std::integral<Ty>)
2962 constexpr bool operator==(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2963 {
2964 return value_compare(rhs) == 0;
2965 }
2970
2974 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2975 constexpr bool operator!=(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2976 {
2977 return !(*this == rhs);
2978 }
2979
2981
2986 constexpr underlying_type raw() const noexcept
2987 {
2988 return static_cast<underlying_type>(NumericalScale::scale(_linearized_value));
2990
2998 constexpr auto value() const noexcept
2999 {
3000 using CfTraits = traits::conversion_factor_traits<ConversionFactor>;
3001
3002 constexpr bool needs_fp = traits::is_ratio_dimensionless_cf_v<ConversionFactor> || !std::ratio_equal_v<typename CfTraits::pi_exponent_ratio, std::ratio<0>> ||
3003 !std::ratio_equal_v<typename CfTraits::translation_ratio, std::ratio<0>>;
3004
3005 using normalized_value_type = std::conditional_t<needs_fp, detail::floating_point_promotion_t<underlying_type>, underlying_type>;
3006
3008 {
3009 // Always normalize dimensionless units to base dimensionless ratio for "value()"
3010 // For ratio-dimensionless (pct/ppm/ppb), we *promote* the return type so int percent works.
3011 using Under = normalized_value_type;
3012
3013 using BaseDimlessCF = units::conversion_factor<std::ratio<1>, dimension::dimensionless>;
3014
3015 using BaseDimlessUnit = unit<BaseDimlessCF, Under, NumericalScale>;
3016
3017 return NumericalScale::scale(units::convert<BaseDimlessUnit>(*this).to_linearized());
3018 }
3019 else
3020 {
3021 return static_cast<normalized_value_type>(raw());
3022 }
3023 }
3024
3025
3029 template<ArithmeticType Ty>
3030 constexpr Ty to() const noexcept
3031 {
3032 return static_cast<Ty>(*this);
3033 }
3034
3037 * @details Converts to a different named unit of the same dimension, e.g.
3038 * `(100.0_cm).to<meters>()`. The named-template spelling of `convert()`; provided so a
3039 * single accessor reads for both underlying-type extraction (`to<double>()`) and
3040 * dimensioned conversion (`to<meters>()`).
3041 * @tparam UnitType unit class template to convert to
3042 * @returns a `UnitType<T>` containing the equivalent value to *this.
3043 */
3044 template<template<class> class UnitType>
3045 requires same_dimension<UnitType<T>, unit>
3046 constexpr UnitType<T> to() const noexcept
3047 {
3048 return UnitType<T>(*this);
3049 }
3050
3055 constexpr T to_linearized() const noexcept
3056 {
3057 return _linearized_value;
3058 }
3059
3062 * @details Converts to a different unit. Units can be converted to other units
3063 * implicitly, but this can be used in cases where the explicit notation of a conversion
3064 * is beneficial, or where an prvalue unit is needed.
3065 * @tparam Cf conversion factor of the unit to convert to
3066 * @tparam Ty underlying type of the unit to convert to
3067 * @returns a unit with the specified parameters containing the equivalent value to
3068 * *this.
3069 */
3070 template<ConversionFactorType Cf, ArithmeticType Ty = T>
3071 constexpr unit<Cf, Ty> convert() const noexcept
3072 {
3073 return unit<Cf, Ty>(*this);
3074 }
3075
3078 * @details Converts to a different unit. Units can be converted to other units
3079 * implicitly, but this can be used in cases where the explicit notation of a conversion
3080 * is beneficial, or where a prvalue unit is needed.
3081 * @tparam UnitType unit type to convert to
3082 * @returns a unit with the specified parameters containing the equivalent value to
3083 * *this.
3084 */
3085 template<template<class> class UnitType>
3086 requires same_dimension<UnitType<T>, unit>
3087 constexpr UnitType<T> convert() const noexcept
3088 {
3089 return UnitType<T>(*this);
3090 }
3091
3095
3096 template<ArithmeticType Ty>
3098 constexpr operator Ty() const noexcept
3099 {
3100 // this conversion also resolves any PI exponents, by converting from a non-zero PI ratio to a zero-pi
3101 // ratio.
3102 return static_cast<Ty>(this->value());
3103 }
3104
3106
3109 template<ArithmeticType Ty>
3111 constexpr explicit operator Ty() const noexcept
3112 {
3113 return static_cast<Ty>(this->value());
3114 }
3115
3117
3120 template<ArithmeticType Rep, RatioType Period, ConversionFactorType Cf = ConversionFactor>
3121 requires detail::is_time_conversion_factor<Cf> && detail::is_losslessly_convertible<T, Rep>
3122 constexpr operator std::chrono::duration<Rep, Period>() const noexcept
3123 {
3124 return std::chrono::duration<Rep, Period>(units::unit<units::conversion_factor<Period, dimension::time>, Rep>(*this).value());
3125 }
3126
3130 template<UnitType Unit = unit>
3131 [[nodiscard]] constexpr const char* name() const noexcept
3132 {
3133 // unit_name is specialized on the NAMED class, not this unit<...> base; resolve the named form first so a
3134 // named unit (feet) reports "feet" instead of null. A compound/unnamed unit has no registered name; report
3135 // the empty string rather than nullptr so the result is always a valid C string to print or copy.
3136 constexpr const char* n = unit_name_v<detail::rewrap_to_named_t<Unit>>;
3137 return n ? n : "";
3138 }
3139
3143 template<UnitType Unit = unit>
3144 [[nodiscard]] constexpr const char* abbreviation() const noexcept
3145 {
3146 // unit_abbreviation is specialized on the NAMED class, not this unit<...> base; resolve the named form
3147 // first so a named unit (feet) reports "ft" instead of null. A compound/unnamed unit has no registered
3148 // abbreviation; report the empty string rather than nullptr so the result is always a valid C string.
3149 constexpr const char* a = unit_abbreviation_v<detail::rewrap_to_named_t<Unit>>;
3150 return a ? a : "";
3151 }
3152
3153 template<ConversionFactorType Cf, ArithmeticType Ty, NumericalScaleType<Ty> Ns>
3154 friend class unit;
3155
3156 private:
3161 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
3162 constexpr auto value_compare(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
3163 {
3164 using CommonUnit = std::common_type_t<unit, unit<ConversionFactorRhs, Ty, NsRhs>>;
3165 if constexpr (std::is_integral_v<T> && std::is_integral_v<Ty>)
3166 {
3167 // Reconcile each side to the common unit's scale in its OWN (sign-preserving) underlying type, then
3168 // compare with std::cmp_* so a mixed-signedness pair orders by value, not by unsigned wraparound.
3169 const T lhsCommon = unit<typename CommonUnit::conversion_factor, T, NumericalScale>(*this)._linearized_value;
3170 const Ty rhsCommon = unit<typename CommonUnit::conversion_factor, Ty, NsRhs>(rhs)._linearized_value;
3171 if (std::cmp_less(lhsCommon, rhsCommon))
3172 return std::strong_ordering::less;
3173 if (std::cmp_greater(lhsCommon, rhsCommon))
3174 return std::strong_ordering::greater;
3175 return std::strong_ordering::equal;
3176 }
3177 else
3178 {
3179 const auto lhsCommon = CommonUnit(*this)._linearized_value;
3180 const auto rhsCommon = CommonUnit(rhs)._linearized_value;
3181 return lhsCommon <=> rhsCommon;
3182 }
3183 }
3184
3185 public:
3188 T _linearized_value;
3189 };
3190
3191 namespace detail
3192 {
3204
3205 // True iff T is a unit-derived class that is NOT itself the canonical unit<...> (i.e. a NAMED unit). Guarded:
3206 // unit_base_t<T> (which reads T::conversion_factor) is only well-formed for a unit, so gate on is_unit FIRST
3207 // via a helper struct — a plain arithmetic T (e.g. double) has no conversion_factor and must yield false, not
3208 // a hard error.
3209 template<class T, bool = traits::is_unit<T>::value>
3210 struct is_named_unit_impl : std::false_type
3211 {
3212 };
3213 template<class T>
3214 struct is_named_unit_impl<T, true> : std::bool_constant<!std::is_same_v<T, unit_base_t<T>>>
3215 {
3216 };
3217 template<class T>
3218 inline constexpr bool is_named_unit_v = is_named_unit_impl<T>::value;
3219
3220 // Two conversion factors are EQUIVALENT when they describe the same physical mapping — same dimension,
3221 // conversion ratio, pi exponent, and datum — even if they are different C++ types (a flattened
3222 // `conversion_factor<ratio<1,100>, length>` versus the composed `centi<meters_>` that `centimeters` is
3223 // registered as). Type identity is stricter than equivalence; a reconciliation result that is equivalent
3224 // to an operand's unit should still recover that operand's friendly name.
3225 template<class Cf1, class Cf2>
3226 inline constexpr bool is_equivalent_conversion_factor_v =
3227 traits::is_same_dimension_conversion_factor_v<Cf1, Cf2> &&
3228 std::ratio_equal_v<typename Cf1::conversion_ratio, typename Cf2::conversion_ratio> &&
3229 std::ratio_equal_v<typename Cf1::pi_exponent_ratio, typename Cf2::pi_exponent_ratio> &&
3230 std::ratio_equal_v<typename Cf1::translation_ratio, typename Cf2::translation_ratio>;
3231
3232 // A conversion factor is RAW when it carries no registered name of its own — a bare reconciliation result
3233 // such as the flattened gcd of meters and centimeters, for which `named_class_of` finds no registration and
3234 // `rewrap_to_named` is the identity. A named unit's registered factor (meters_, joules_, …) is NOT raw: it
3235 // resolves to its named class. Equivalence-based name recovery fires only for a RAW factor, because
3236 // recovering a name for an already-named factor could rename one physical kind to another that shares its
3237 // dimension and ratio (torque's newton_meters_ and energy's joules_ are equivalent) — so recovery is
3238 // restricted to the anonymous reconciliation results that have no name to preserve.
3239 template<class Cf>
3240 inline constexpr bool is_raw_conversion_factor_v =
3241 std::is_void_v<decltype(named_class_of(static_cast<Cf*>(nullptr), static_cast<linear_scale*>(nullptr)))>;
3242
3243 // Re-wrap a computed base result `unit<Cf, U, Ns>` into a NAMED unit when a candidate operand `Named` is a
3244 // named unit of the SAME conversion_factor: the friendly name is preserved through the trait (so
3245 // common_type<meters<int>, meters<double>> is meters<double>, not unit<meters_, double, linear_scale>). When no
3246 // candidate matches (mixed names, or a plain-unit operand), the base result stands. `Base` is the plain unit<>.
3247 template<class Base, class Named, class = void>
3248 struct rewrap_named
3249 {
3250 using type = Base;
3251 };
3252 template<class Base, class Named>
3253 struct rewrap_named<Base, Named,
3254 std::enable_if_t<is_named_unit_v<Named> && std::is_same_v<typename Base::conversion_factor, typename Named::conversion_factor>>>
3255 {
3256 using type = typename Named::template rebind<typename Base::underlying_type>;
3257 };
3258 // Equivalence recovery: when `Base`'s factor is RAW (an anonymous reconciliation result, e.g. the flattened
3259 // gcd of meters and centimeters) and is equivalent to a named operand's factor, recover that operand's name.
3260 // This names an m − cm result `centimeters` and an hr − min result `minutes` where exact-type matching missed
3261 // them, without renaming an already-named result (the raw guard excludes strong factors such as joules_).
3262 template<class Base, class Named>
3263 struct rewrap_named<Base, Named,
3264 std::enable_if_t<is_named_unit_v<Named> && !std::is_same_v<typename Base::conversion_factor, typename Named::conversion_factor> &&
3265 is_raw_conversion_factor_v<typename Base::conversion_factor> &&
3266 is_equivalent_conversion_factor_v<typename Base::conversion_factor, typename Named::conversion_factor>>>
3267 {
3268 using type = typename Named::template rebind<typename Base::underlying_type>;
3269 };
3270 template<class Base, class Named>
3271 using rewrap_named_t = typename rewrap_named<Base, Named>::type;
3272
3273 // Identity fallback for the CF-struct -> named-class ADL map (the exact registrations are emitted per named
3274 // unit by UNIT_REGISTER_NAMED_CLASS). Worst match (trailing ellipsis); returns void to signal "no named class
3275 // for this CF". decltype-only, never defined. A real registration's exact strong-CF* parameter beats this.
3276 template<class ConversionFactor, class Scale>
3277 void named_class_of(ConversionFactor*, Scale*, ...);
3278
3279 // Map a plain unit<Cf, U, Ns> to its NAMED class when one is registered for Cf, else identity. Used by the
3280 // arithmetic operators so a computed result (e.g. unit<square_meters_, int, linear_scale>) is REPORTED as the
3281 // friendly named type (square_meters<int>). Rebinds the registered class to U so the underlying flows through.
3282 // SFINAE-guarded: only a unit whose Cf has a registration is rewrapped; everything else is identity.
3283 // (The primary template + the rewrap_to_named_t alias are forward-declared before the unit class so unit's
3284 // name()/abbreviation() members can name them; here we DEFINE the primary and the specialization.)
3285 template<class U, class>
3286 struct rewrap_to_named
3287 {
3288 using type = U;
3289 };
3290 template<class U>
3291 struct rewrap_to_named<U,
3292 std::enable_if_t<traits::is_unit<U>::value &&
3293 !std::is_void_v<decltype(named_class_of(static_cast<typename U::conversion_factor*>(nullptr),
3294 static_cast<typename U::numerical_scale_type*>(nullptr)))>>>
3295 {
3296 using type = typename decltype(named_class_of(static_cast<typename U::conversion_factor*>(nullptr),
3297 static_cast<typename U::numerical_scale_type*>(nullptr)))::template rebind<typename U::underlying_type>;
3298 };
3299 } // namespace detail
3300
3301 namespace traits
3302 {
3303 // A NAMED unit (a class deriving from unit<...>) unwraps to its base for these exact-pattern traits, so
3304 // replace_underlying / floating_point_promotion behave for named units exactly as for the plain unit<...>.
3305 // The plain-unit<...> specializations are declared earlier; these constrained ones fire only for a named unit.
3306 template<class Unit, class Underlying>
3307 requires ::units::detail::is_named_unit_v<Unit>
3308 struct replace_underlying<Unit, Underlying>
3309 {
3310 // PRESERVE the named type: rebind it to the new underlying (meters<int> -> meters<double>), rather than
3311 // decaying to the plain unit<...> base. Keeps trait results as friendly as the inputs.
3312 using type = typename Unit::template rebind<Underlying>;
3313 };
3314 } // namespace traits
3315
3316 namespace detail
3317 {
3318 template<class Unit>
3319 requires is_named_unit_v<Unit>
3320 struct floating_point_promotion<Unit>
3321 {
3322 // Promote the UNDERLYING type but PRESERVE the friendly named type: rebind the named unit to the promoted
3323 // underlying (meters<int> -> meters<double>), so ceil/floor/round/hypot report the named result, not unit<>.
3324 using type = typename Unit::template rebind<typename floating_point_promotion<unit_base_t<Unit>>::type::underlying_type>;
3325 };
3326 } // namespace detail
3327
3328 //------------------------------
3329 // UNIT NON-MEMBER FUNCTIONS
3330 //------------------------------
3331
3335 * @details make_unit can be used to construct a unit container from an arithmetic type, as an alternative to
3336 * using the explicit constructor. Unlike the explicit constructor it forces the user to explicitly
3337 * specify the units.
3338 * @tparam UnitType Type to construct.
3339 * @tparam T Arithmetic type.
3340 * @param[in] value Arithmetic value that represents a quantity in units of `UnitType`.
3341 */
3342 template<UnitType UnitType, ArithmeticType T>
3343 requires detail::is_losslessly_convertible<T, typename UnitType::underlying_type>
3344 constexpr UnitType make_unit(const T value) noexcept
3345 {
3346 return UnitType(value);
3347 }
3348
3349 //-----------------------------------------
3350 // UNIT-LABEL STRING BUILDERS
3351 //-----------------------------------------
3352
3353#if defined(UNIT_LIB_ENABLE_STRING)
3354
3355 namespace detail
3356 {
3357 //----------------------------------------------------------------------------------------------------------------------
3358 // FUNCTION: dimension_to_string [static]
3359 //----------------------------------------------------------------------------------------------------------------------
3366 //----------------------------------------------------------------------------------------------------------------------
3367 template<class D, class E>
3368 std::string dimension_to_string(const dim<D, E>&)
3369 {
3370 std::string s;
3371 if constexpr (E::num != 0)
3372 {
3373 s.append(" ").append(D::abbreviation);
3374 }
3375 if constexpr (E::num != 0 && E::num != 1)
3376 {
3377 s.append("^").append(std::to_string(E::num));
3378 }
3379 if constexpr (E::den != 1)
3380 {
3381 s.append("/").append(std::to_string(E::den));
3382 }
3383 return s;
3384 }
3386 //----------------------------------------------------------------------------------------------------------------------
3387 // FUNCTION: dimension_to_string [static]
3388 //----------------------------------------------------------------------------------------------------------------------
3391
3392 //----------------------------------------------------------------------------------------------------------------------
3393 template<class... Dims>
3394 std::string dimension_to_string(const dimension_t<Dims...>&)
3395 {
3396 std::string s;
3397 ((s.append(dimension_to_string(Dims{}))), ...);
3398 return s;
3399 }
3400
3401 //----------------------------------------------------------------------------------------------------------------------
3402 // FUNCTION: unit_label [static]
3403 //----------------------------------------------------------------------------------------------------------------------
3415 //----------------------------------------------------------------------------------------------------------------------
3421
3424 enum class label_form
3425 {
3426 abbreviation,
3427 name,
3428 base
3429 };
3430
3431 template<label_form Form = label_form::abbreviation, ConversionFactorType ConversionFactor, ArithmeticType T, NumericalScaleType<T> NumericalScale>
3432 std::string unit_label(const unit<ConversionFactor, T, NumericalScale>&)
3433 {
3434 // The name/abbreviation traits are specialized on the NAMED class, not the plain unit<...> base,
3435 // so resolve the named form first and query THAT (a named unit prints its name/abbreviation).
3436 using NamedForm = detail::rewrap_to_named_t<unit<ConversionFactor, T, NumericalScale>>;
3438
3439 if constexpr (Form == label_form::base)
3440 {
3441 // SI base-dimension list, regardless of the unit's own name (the caller base-converts the value).
3442 if constexpr (!DimType::empty)
3443 return dimension_to_string(DimType{});
3444 else
3445 return std::string{};
3446 }
3447 else if constexpr (Form == label_form::name && unit_name_v<NamedForm>)
3448 {
3449 return std::string(" ").append(unit_name<NamedForm>::value);
3450 }
3451 else if constexpr (unit_abbreviation_v<NamedForm>)
3452 {
3453 return std::string(" ").append(unit_abbreviation<NamedForm>::value);
3454 }
3455 else
3456 {
3457 // Unnamed unit: its honest label IS the base-dimension list (no own symbol exists).
3458 if constexpr (!DimType::empty)
3459 return dimension_to_string(DimType{});
3460 else
3461 return std::string{};
3462 }
3463 }
3464
3465 //----------------------------------------------------------------------------------------------------------------------
3466 // FUNCTION: label_uses_base_unit [static]
3467 //----------------------------------------------------------------------------------------------------------------------
3469 /// @details An unnamed unit is rendered in its BASE unit (its value must be converted to the base
3470 /// before the dimension label applies); a named unit prints its value as-is. This
3471 /// predicate lets the value-rendering paths decide whether to convert to the base unit.
3472 /// @tparam ConversionFactor the unit's conversion factor.
3473 /// @tparam T the unit's underlying arithmetic type.
3474 /// @tparam NumericalScale the unit's numerical scale.
3475 /// @return `true` when the unit is unnamed (dimension-labelled), `false` when it is named.
3476 //----------------------------------------------------------------------------------------------------------------------
3477 template<ConversionFactorType ConversionFactor, ArithmeticType T, NumericalScaleType<T> NumericalScale>
3478 inline constexpr bool label_uses_base_unit()
3479 {
3480 using NamedForm = detail::rewrap_to_named_t<unit<ConversionFactor, T, NumericalScale>>;
3481 return !static_cast<bool>(unit_abbreviation_v<NamedForm>);
3482 }
3483 } // namespace detail
3484
3485#endif // UNIT_LIB_ENABLE_STRING
3486
3487#if defined(UNIT_LIB_ENABLE_FORMAT)
3488
3489 //-----------------------------------------
3490 // std::format SUPPORT
3491 //-----------------------------------------
3492
3493 namespace detail
3494 {
3495 //----------------------------------------------------------------------------------------------------------------------
3496 // STRUCT: unit_format_options
3497 //----------------------------------------------------------------------------------------------------------------------
3499 //----------------------------------------------------------------------------------------------------------------------
3500 struct unit_format_options
3501 {
3502 label_form form = label_form::abbreviation;
3503 bool showValue = true;
3504 bool showUnit = true;
3505 bool customSep = false;
3506 std::string separator = " ";
3507 };
3508 } // namespace detail
3509
3510#endif // UNIT_LIB_ENABLE_FORMAT
3511
3512#if !defined(UNIT_LIB_DISABLE_IOSTREAM)
3513
3514 //-----------------------------------------
3515 // OSTREAM OPERATOR FOR EPHEMERAL UNITS
3516 //-----------------------------------------
3517
3518 template<class D, class E>
3519 std::ostream& operator<<(std::ostream& os, const dim<D, E>&)
3520 {
3521 if constexpr (E::num != 0)
3522 os << ' ' << D::abbreviation;
3523 if constexpr (E::num != 0 && E::num != 1)
3524 {
3525 os << "^" << E::num;
3526 }
3527 if constexpr (E::den != 1)
3528 {
3529 os << "/" << E::den;
3530 }
3531 return os;
3532 }
3533
3534 template<class... Dims>
3535 std::ostream& operator<<(std::ostream& os, const dimension_t<Dims...>&)
3536 {
3537 ((os << Dims{}), ...);
3538 return os;
3539 }
3540
3541 template<ConversionFactorType ConversionFactor, ArithmeticType T, NumericalScaleType<T> NumericalScale>
3542 std::ostream& operator<<(std::ostream& os, const unit<ConversionFactor, T, NumericalScale>& obj)
3543 {
3544 using BaseConversion = conversion_factor<std::ratio<1>, typename ConversionFactor::dimension_type>;
3546 using PromotedBaseUnit = unit<BaseConversion, detail::floating_point_promotion_t<T>, NumericalScale>;
3547
3548 // The abbreviation trait is specialized on the NAMED class, not the plain unit<...> base this overload
3549 // deduces; resolve the named form first and query THAT so a named unit (meters_per_second -> "mps") prints
3550 // its abbreviation instead of the dimension form.
3551 using NamedForm = detail::rewrap_to_named_t<unit<ConversionFactor, T, NumericalScale>>;
3552
3553 if constexpr (unit_abbreviation_v<NamedForm>)
3554 {
3555 os << obj.raw();
3556 }
3557 else
3558 {
3559 os << std::conditional_t<detail::is_losslessly_convertible_unit<std::decay_t<decltype(obj)>, BaseUnit>, BaseUnit, PromotedBaseUnit>(obj).raw();
3560 }
3561 os << detail::unit_label(obj);
3562
3563 return os;
3564 }
3565
3566 //----------------------------
3567 // to_string
3568 //----------------------------
3569
3570 template<ConversionFactorType ConversionFactor, ArithmeticType T, NumericalScaleType<T> NumericalScale>
3571 std::string to_string(const unit<ConversionFactor, T, NumericalScale>& obj)
3572 {
3573 using BaseConversion = conversion_factor<std::ratio<1>, typename ConversionFactor::dimension_type>;
3575 using PromotedBaseUnit = unit<BaseConversion, detail::floating_point_promotion_t<T>, NumericalScale>;
3576
3577 // The abbreviation trait (unit_name/unit_abbreviation) is specialized on the NAMED class, not the plain
3578 // unit<...> base this overload deduces, so resolve the named form first and query THAT — a named unit
3579 // (feet<double>) then still prints its abbreviation ("ft") instead of falling to the dimension path.
3580 using NamedForm = detail::rewrap_to_named_t<unit<ConversionFactor, T, NumericalScale>>;
3581
3582 std::string s;
3583 if constexpr (unit_abbreviation_v<NamedForm>)
3584 s = detail::to_string(obj.raw());
3585 else
3586 s = detail::to_string(std::conditional_t<detail::is_losslessly_convertible_unit<std::decay_t<decltype(obj)>, BaseUnit>, BaseUnit, PromotedBaseUnit>(obj).raw());
3587
3588 s.append(detail::unit_label(obj));
3589 return s;
3590 }
3591#endif
3592
3593 //------------------------------
3594 // std::ratio helpers
3595 //------------------------------
3596 // DOXYGEN IGNORE
3598 namespace detail
3599 {
3603 template<RatioType Ratio1, RatioType Ratio2>
3604 using ratio_gcd = std::ratio<std::gcd(Ratio1::num, Ratio2::num), std::lcm(Ratio1::den, Ratio2::den)>;
3605
3612 template<RatioType Ratio1, RatioType Ratio2>
3613 using common_baggage_ratio = std::conditional_t<std::ratio_equal_v<Ratio1, Ratio2>, Ratio1, std::ratio<0>>;
3614 } // namespace detail // END DOXYGEN IGNORE
3616} // end namespace units
3617
3618//------------------------------
3619// std::common_type
3620//------------------------------
3621
3622namespace std
3623{
3624 /**
3625 * @ingroup STDTypeTraits
3626 * @brief common type of units
3627 * @details The `type` alias of the `std::common_type` of two `unit`s of the same dimension is the least precise
3628 * `unit` to which both `unit` arguments can be converted to without requiring a division operation or
3629 * truncating any value of these conversions, although floating-point units may have round-off errors.
3630 * If the units have mixed scales, preference is given to `linear_scale` for their common type.
3631 */
3632 template<class ConversionFactorLhs, class Tx, class ConversionFactorRhs, class Ty, class NumericalScale>
3633 struct common_type<units::unit<ConversionFactorLhs, Tx, NumericalScale>, units::unit<ConversionFactorRhs, Ty, NumericalScale>>
3634 : std::enable_if<units::traits::is_same_dimension_conversion_factor_v<ConversionFactorLhs, ConversionFactorRhs>,
3635 units::unit<
3636 units::traits::strong_t<units::conversion_factor<units::detail::ratio_gcd<typename ConversionFactorLhs::conversion_ratio, typename ConversionFactorRhs::conversion_ratio>,
3637 units::traits::dimension_of_t<ConversionFactorLhs>, units::detail::ratio_gcd<typename ConversionFactorLhs::pi_exponent_ratio, typename ConversionFactorRhs::pi_exponent_ratio>,
3638 units::detail::common_baggage_ratio<typename ConversionFactorLhs::translation_ratio, typename ConversionFactorRhs::translation_ratio>>>,
3639 common_type_t<Tx, Ty>, NumericalScale>>
3640 {
3641 };
3642
3643 // In the case the two units are the same type, just use that type as common type
3644 template<class UnitConversionT, class T, class NonLinearScale>
3645 struct common_type<units::unit<UnitConversionT, T, NonLinearScale>, units::unit<UnitConversionT, T, NonLinearScale>>
3646 {
3648 };
3649
3650 // A NAMED unit is a class deriving from unit<...>; the exact-pattern specializations above do not match it. When
3651 // either operand is a named unit, compute the common type of the canonical unit<...> BASES, then RE-WRAP the result
3652 // into the named type when an operand shares its conversion_factor — so common_type<meters<int>, meters<double>> is
3653 // meters<double>, not the plain unit<...> (the friendly name survives through the trait). Constrained to "both are
3654 // units AND at least one is named" so it never overlaps the exact-unit<...> cases above.
3655 template<class Lhs, class Rhs>
3657 (units::detail::is_named_unit_v<Lhs> || units::detail::is_named_unit_v<Rhs>) &&
3658 // ONLY when the plain-base common type EXISTS (same dimension). For different dimensions the bases have
3659 // no common type, so this specialization must be SFINAE-EMPTY too (no `type`) — matching the plain
3660 // unit<...> behavior. Without this, computing `base` below is a hard error on stricter compilers
3661 // (clang) where g++ tolerated the absent member.
3662 requires { typename common_type<units::detail::unit_base_t<Lhs>, units::detail::unit_base_t<Rhs>>::type; })
3663 struct common_type<Lhs, Rhs>
3664 {
3665 private:
3666 using base = common_type_t<units::detail::unit_base_t<Lhs>, units::detail::unit_base_t<Rhs>>;
3667 // prefer to re-wrap into Lhs's name; if that doesn't share the CF, try Rhs's.
3668 using viaLhs = units::detail::rewrap_named_t<base, Lhs>;
3669
3670 public:
3671 using type = units::detail::rewrap_named_t<viaLhs, Rhs>;
3672 };
3673
3674 // A NAMED DIMENSIONLESS unit (e.g. percent) mixed with a plain arithmetic scalar: the exact-unit<...>-vs-scalar
3675 // specializations below do not match the named class, so unwrap the named operand to its base and re-wrap the
3676 // result to keep the friendly name. dimensionless units stay fully interchangeable with int/double. Gated on
3677 // is_dimensionless_unit (mirroring the plain unit<...>-vs-scalar specializations): a DIMENSIONED named unit + a
3678 // scalar must NOT match — it falls through to the primary std::common_type and is SFINAE-empty (no `type`), the
3679 // same SFINAE-friendly behavior the plain form has (never a hard error).
3680 template<class Named, class Scalar>
3681 requires(units::detail::is_named_unit_v<Named> && std::is_arithmetic_v<Scalar> &&
3683 struct common_type<Named, Scalar>
3684 {
3685 using type = units::detail::rewrap_named_t<common_type_t<units::detail::unit_base_t<Named>, Scalar>, Named>;
3686 };
3687 template<class Scalar, class Named>
3688 requires(units::detail::is_named_unit_v<Named> && std::is_arithmetic_v<Scalar> &&
3690 struct common_type<Scalar, Named>
3691 {
3692 using type = units::detail::rewrap_named_t<common_type_t<Scalar, units::detail::unit_base_t<Named>>, Named>;
3694
3695 template<class Ratio, class T, class NumericalScale, class Rep, class Period>
3696 struct common_type<units::unit<units::detail::time_conversion_factor<Ratio>, T, NumericalScale>, chrono::duration<Rep, Period>>
3697 : std::common_type<units::unit<units::detail::time_conversion_factor<Ratio>, T, NumericalScale>, decltype(units::unit{chrono::duration<Rep, Period>{}})>
3698 {
3699 };
3701 template<class ConversionFactor, class T, class NumericalScale, class Rep, class Period>
3702 struct common_type<chrono::duration<Rep, Period>, units::unit<ConversionFactor, T, NumericalScale>>
3703 : std::common_type<units::unit<ConversionFactor, T, NumericalScale>, chrono::duration<Rep, Period>>
3704 {
3705 };
3706
3707 template<class ConversionFactor, class Tx, class NumericalScale, class Ty>
3708 requires std::is_arithmetic_v<Ty> // constrain so a unit `Ty` never matches (that is a unit+unit case above)
3709 struct common_type<Ty, units::unit<ConversionFactor, Tx, NumericalScale>>
3710 : std::enable_if<units::traits::is_dimensionless_unit<units::unit<ConversionFactor, Tx, NumericalScale>>::value,
3711 units::unit<units::conversion_factor<std::ratio<1>, units::dimension::dimensionless>, common_type_t<Tx, Ty>, NumericalScale>>
3712 {
3713 };
3714
3715 template<class ConversionFactor, class Tx, class NumericalScale, class Ty>
3716 requires std::is_arithmetic_v<Ty> // constrain so a unit `Ty` never matches (that is a unit+unit case above)
3717 struct common_type<units::unit<ConversionFactor, Tx, NumericalScale>, Ty>
3718 : std::enable_if<units::traits::is_dimensionless_unit<units::unit<ConversionFactor, Tx, NumericalScale>>::value,
3719 units::unit<units::conversion_factor<std::ratio<1>, units::dimension::dimensionless>, common_type_t<Tx, Ty>, NumericalScale>>
3720 {
3721 };
3722 // DOXYGEN IGNORE
3727 template<class ConversionFactorLhs, class Tx, class ConversionFactorRhs, class Ty>
3728 struct common_type<units::unit<ConversionFactorLhs, Tx, units::linear_scale>, units::unit<ConversionFactorRhs, Ty, units::decibel_scale>>
3729 : common_type<units::unit<ConversionFactorLhs, Tx, units::linear_scale>, units::unit<ConversionFactorRhs, Ty, units::linear_scale>>
3730 {
3731 };
3732
3733 template<class ConversionFactorLhs, class Tx, class ConversionFactorRhs, class Ty>
3734 struct common_type<units::unit<ConversionFactorLhs, Tx, units::decibel_scale>, units::unit<ConversionFactorRhs, Ty, units::linear_scale>>
3735 : common_type<units::unit<ConversionFactorLhs, Tx, units::linear_scale>, units::unit<ConversionFactorRhs, Ty, units::linear_scale>>
3736 {
3737 };
3738 // END DOXYGEN IGNORE
3740} // namespace std
3741
3742namespace units
3743{
3744 //------------------------------
3745 // UNIT_CAST
3746 //------------------------------
3747
3754 * @code meter_t unitVal(5);
3755 * double value = units::unit_cast<double>(unitVal); // value == 5.0
3756 * @endcode
3757 * @tparam T Type to cast the unit type to. Shall be an arithmetic type.
3758 * @tparam Unit Type of the unit to cast to.
3759 * @param value Unit value to cast.
3760 * @sa unit::to
3761 */
3762 template<ArithmeticType T, UnitType Unit>
3763 constexpr T unit_cast(const Unit& value) noexcept
3764 {
3765 return static_cast<T>(value);
3766 }
3767
3768 //------------------------------
3769 // NUMERICAL SCALE TRAITS
3770 //------------------------------
3771
3772 // forward declaration
3773 namespace traits
3777
3782 template<typename... T>
3783 struct has_linear_scale : std::conjunction<std::is_base_of<linear_scale, T>...>
3784 {
3785 };
3786
3787 template<typename... T>
3788 inline constexpr bool has_linear_scale_v = has_linear_scale<T...>::value;
3792
3797 template<typename... T>
3798 struct has_decibel_scale : std::conjunction<std::is_base_of<decibel_scale, T>...>
3799 {
3800 };
3801
3802 template<typename... T>
3803 inline constexpr bool has_decibel_scale_v = has_decibel_scale<T...>::value;
3804 } // namespace traits
3805
3806 //----------------------------------
3807 // NUMERICAL SCALES
3808 //----------------------------------
3809
3810 // Non-linear transforms may be used to pre- and post-scale units which are defined in terms of non-
3811 // linear functions of their current value. A good example of a non-linear scale would be a
3812 // logarithmic or decibel scale
3813
3814 //------------------------------
3815 // LINEAR SCALE
3816 //------------------------------
3817
3825 {
3828
3832 template<class T>
3833 static constexpr T linearize(const T value) noexcept
3834 {
3835 return value;
3837
3840
3841
3844 template<class T>
3845 static constexpr T scale(const T value) noexcept
3846 {
3847 return value;
3848 }
3849 };
3850
3851 //----------------------------------
3852 // dimensionless (LINEAR) UNITS
3853 //----------------------------------
3854
3855 // dimensionless units are the *ONLY* units implicitly convertible to/from built-in types.
3856
3857 using dimensionless_ = conversion_factor<std::ratio<1>, dimension::dimensionless>;
3858
3859 namespace detail
3860 {
3861 // ADL registration of the dimensionless strong type (see detail::strong_name, #357). The base-form CF maps
3862 // back to the canonical dimensionless conversion_factor. Declared, never defined (used only in decltype).
3863 conversion_factor<std::ratio<1>, dimension::dimensionless> strong_name(
3864 units::detail::conversion_factor_base_t<dimensionless_>*);
3866 // The PURE dimensionless unit (ratio 1) stays a plain alias to unit<...>, NOT a named class: it must remain
3867 // totally interchangeable with the built-in arithmetic types (int/double) and identity-equal to its unit<...>
3868 // base (so common_type<dimensionless<int>, int> is the plain unit and dimensionless<int> IS unit<dimensionless_,
3869 // int>). A distinct class would break that interchangeability. Named ratio-dimensionless units (percent/ppm/...)
3870 // are still classes — they carry a meaningful name.
3871 template<class Underlying = UNIT_LIB_DEFAULT_TYPE>
3872 using dimensionless = unit<traits::strong_t<conversion_factor<std::ratio<1>, dimension::dimensionless>>, Underlying, linear_scale>;
3873
3875
3876 //----------------------------------------
3877 // UNIT COMPOUND ASSIGNMENT OPERATORS
3878 //----------------------------------------
3879
3880 // DOXYGEN IGNORE
3881 namespace detail
3882 {
3886 template<class T>
3887 struct type_identity
3888 {
3889 using type = T;
3890 };
3891
3892 template<class T>
3893 using type_identity_t = typename type_identity<T>::type;
3894 } // namespace detail // END DOXYGEN IGNORE
3896
3897 template<UnitType UnitTypeLhs>
3899 constexpr UnitTypeLhs& operator+=(UnitTypeLhs& lhs, const detail::type_identity_t<UnitTypeLhs>& rhs) noexcept
3900 {
3901 lhs = lhs + rhs;
3902 return lhs;
3904
3908
3910 template<UnitType UnitTypeLhs>
3912 constexpr UnitTypeLhs& operator+=(UnitTypeLhs& lhs, const detail::type_identity_t<UnitTypeLhs>& rhs) noexcept
3913 {
3914 lhs = UnitTypeLhs(lhs.raw() + rhs.raw());
3915 return lhs;
3916 }
3917
3918 template<UnitType UnitTypeLhs, ArithmeticType T>
3920 constexpr UnitTypeLhs& operator+=(UnitTypeLhs& lhs, T rhs) noexcept
3921 {
3922 lhs = lhs + rhs;
3923 return lhs;
3924 }
3925
3926 template<RatioDimensionlessUnitType U, ArithmeticType T>
3927 requires(traits::has_linear_scale_v<U>)
3928 constexpr U& operator+=(U& lhs, T rhs) noexcept
3929 {
3930 using Underlying = typename U::underlying_type;
3931 using R = typename U::conversion_factor::conversion_ratio;
3932
3933 // points_per_one converts "fraction-space 1.0" into "points" for this unit.
3934 // Example: percent ratio = 1/100 -> points_per_one = 100
3935 constexpr long double points_per_one = static_cast<long double>(R::den) / static_cast<long double>(R::num);
3936
3937 // Do the math in points space to avoid truncation of lhs.value() for integral percent.
3938 const long double new_points = static_cast<long double>(lhs.raw()) + (static_cast<long double>(rhs) * points_per_one);
3939
3940 if constexpr (std::is_integral_v<Underlying>)
3941 {
3942 lhs = U(static_cast<Underlying>(std::llround(new_points)));
3943 }
3944 else
3945 {
3946 lhs = U(static_cast<Underlying>(new_points));
3947 }
3948
3949 return lhs;
3950 }
3951
3952 template<RatioDimensionlessUnitType U, DimensionlessUnitType D>
3953 requires(traits::has_linear_scale_v<U, D> && !RatioDimensionlessUnitType<D>)
3954 constexpr U& operator+=(U& lhs, const D& rhs) noexcept
3955 {
3956 // rhs.value() is plain scalar (e.g. dimensionless<int>(1) => 1)
3957 return (lhs += rhs.value());
3958 }
3959
3960 template<RatioDimensionlessUnitType U, ArithmeticType T>
3961 requires(traits::has_linear_scale_v<U>)
3962 constexpr U& operator-=(U& lhs, T rhs) noexcept
3963 {
3964 using Underlying = typename U::underlying_type;
3965 using R = typename U::conversion_factor::conversion_ratio;
3966
3967 constexpr long double points_per_one = static_cast<long double>(R::den) / static_cast<long double>(R::num);
3968
3969 const long double new_points = static_cast<long double>(lhs.raw()) - (static_cast<long double>(rhs) * points_per_one);
3970
3971 if constexpr (std::is_integral_v<Underlying>)
3972 {
3973 lhs = U(static_cast<Underlying>(std::llround(new_points)));
3974 }
3975 else
3976 {
3977 lhs = U(static_cast<Underlying>(new_points));
3978 }
3979
3980 return lhs;
3981 }
3982
3983 template<UnitType UnitTypeLhs>
3985 constexpr UnitTypeLhs& operator-=(UnitTypeLhs& lhs, const detail::type_identity_t<UnitTypeLhs>& rhs) noexcept
3986 {
3987 lhs = lhs - rhs;
3988 return lhs;
3990
3994
3996 template<UnitType UnitTypeLhs>
3998 constexpr UnitTypeLhs& operator-=(UnitTypeLhs& lhs, const detail::type_identity_t<UnitTypeLhs>& rhs) noexcept
3999 {
4000 lhs = UnitTypeLhs(lhs.raw() - rhs.raw());
4001 return lhs;
4002 }
4003
4004 template<UnitType UnitTypeLhs, ArithmeticType T>
4006 constexpr UnitTypeLhs& operator-=(UnitTypeLhs& lhs, const T& rhs) noexcept
4007 {
4008 lhs = lhs - rhs;
4009 return lhs;
4010 }
4011
4012 template<RatioDimensionlessUnitType U, DimensionlessUnitType D>
4013 requires(traits::has_linear_scale_v<U, D> && !RatioDimensionlessUnitType<D>)
4014 constexpr U& operator-=(U& lhs, const D& rhs) noexcept
4015 {
4016 return (lhs -= rhs.value());
4017 }
4018
4019 template<UnitType UnitTypeLhs, ArithmeticType T>
4021 constexpr UnitTypeLhs& operator*=(UnitTypeLhs& lhs, const T& rhs)
4022 {
4023 // The rhs is taken as its own arithmetic type (not narrowed to the lhs underlying type at the call
4024 // boundary), so a value-narrowing scale (e.g. meters<int> *= 2.0) applies normal conversion rules. The
4025 // narrowing is performed by an implicit conversion into a local of the lhs's underlying type, which surfaces
4026 // the compiler's -Wfloat-conversion warning naming `meters<int>::underlying_type (aka int)` rather than
4027 // truncating silently; it is a warning, not an error, and the result stays a UnitTypeLhs.
4028 typename UnitTypeLhs::underlying_type scaled = lhs.raw() * rhs;
4029 lhs = UnitTypeLhs(scaled, linearized_value);
4030 return lhs;
4031 }
4032
4033 template<RatioDimensionlessUnitType U, RatioDimensionlessUnitType URhs>
4034 requires(traits::has_linear_scale_v<U, URhs>)
4035 constexpr U& operator*=(U& lhs, const URhs& rhs) noexcept
4036 {
4037 using LhsUnder = typename U::underlying_type;
4038 using RhsUnder = typename URhs::underlying_type;
4039
4040 using Calc0 = std::common_type_t<LhsUnder, RhsUnder>;
4041 using Calc = detail::floating_point_promotion_t<Calc0>;
4042
4043 // rhs interpreted as normalized fraction (e.g. 200_pct -> 2.0, 2_pct -> 0.02)
4044 const Calc rhs_frac = static_cast<Calc>(rhs.value());
4045
4046 // lhs.raw() is "points" (e.g. 12_pct raw() == 12)
4047 const Calc new_points = static_cast<Calc>(lhs.raw()) * rhs_frac;
4048
4049 if constexpr (std::is_integral_v<LhsUnder>)
4050 {
4051 // Deterministic: truncate toward zero
4052 lhs = U(static_cast<LhsUnder>(new_points));
4053 }
4054 else
4055 {
4056 lhs = U(static_cast<LhsUnder>(new_points));
4057 }
4058
4059 return lhs;
4060 }
4061
4062 template<RatioDimensionlessUnitType U>
4063 requires(units::traits::has_linear_scale_v<U>)
4064 constexpr U& operator*=(U& lhs, const U& rhs) noexcept
4065 {
4066 using Underlying = typename U::underlying_type;
4067 using R = typename U::conversion_factor::conversion_ratio;
4068
4069 // percent: 1/100 -> points_per_one = 100
4070 constexpr long double points_per_one = static_cast<long double>(R::den) / static_cast<long double>(R::num);
4071
4072 const long double lhs_frac = static_cast<long double>(lhs.value()); // normalized fraction
4073 const long double rhs_frac = static_cast<long double>(rhs.value()); // normalized fraction
4074
4075 const long double out_frac = lhs_frac * rhs_frac;
4076 const long double out_points = out_frac * points_per_one;
4077
4078 if constexpr (std::is_integral_v<Underlying>)
4079 {
4080 lhs = U(static_cast<Underlying>(std::llround(out_points)));
4081 }
4082 else
4083 {
4084 lhs = U(static_cast<Underlying>(out_points));
4085 }
4086
4087 return lhs;
4088 }
4089
4090 template<RatioDimensionlessUnitType U, units::ArithmeticType T>
4091 requires(units::traits::has_linear_scale_v<U>)
4092 constexpr U& operator*=(U& lhs, T rhs) noexcept
4093 {
4094 // scalar is interpreted as base-dimensionless fraction (world-2)
4095 // so rhs = 2 means multiply fraction by 2
4096 using Underlying = typename U::underlying_type;
4097 using R = typename U::conversion_factor::conversion_ratio;
4098
4099 constexpr long double points_per_one = static_cast<long double>(R::den) / static_cast<long double>(R::num);
4100
4101 const long double lhs_frac = static_cast<long double>(lhs.value());
4102 const long double out_frac = lhs_frac * static_cast<long double>(rhs);
4103 const long double out_pts = out_frac * points_per_one;
4104
4105 if constexpr (std::is_integral_v<Underlying>)
4106 {
4107 lhs = U(static_cast<Underlying>(std::llround(out_pts)));
4108 }
4109 else
4110 {
4111 lhs = U(static_cast<Underlying>(out_pts));
4112 }
4113 return lhs;
4114 }
4115
4116 template<RatioDimensionlessUnitType U, DimensionlessUnitType D>
4117 requires(units::traits::has_linear_scale_v<U, D> && !RatioDimensionlessUnitType<D>)
4118 constexpr U& operator*=(U& lhs, const D& rhs) noexcept
4119 {
4120 // dimensionless is a scalar fraction; use its numeric value
4121 return (lhs *= rhs.value());
4122 }
4123
4124 // scale a dimensioned quantity by a dimensionless quantity: use its numeric value and route through the
4125 // arithmetic overload above (preserves the warn-on-lossy-integer-scale behavior)
4126 template<UnitType UnitTypeLhs, DimensionlessUnitType D>
4128 constexpr UnitTypeLhs& operator*=(UnitTypeLhs& lhs, const D& rhs)
4129 {
4130 return (lhs *= rhs.value());
4131 }
4132
4133 template<UnitType UnitTypeLhs, ArithmeticType T>
4135 constexpr UnitTypeLhs& operator/=(UnitTypeLhs& lhs, const T& rhs)
4136 {
4137 // see operator*= above: a floating-point divisor narrowing an integer-underlying quantity surfaces
4138 // -Wfloat-conversion via the implicit narrow into a local of the lhs underlying type
4139 typename UnitTypeLhs::underlying_type scaled = lhs.raw() / rhs;
4140 lhs = UnitTypeLhs(scaled, linearized_value);
4141 return lhs;
4142 }
4143
4144 template<UnitType UnitTypeLhs, DimensionlessUnitType D>
4146 constexpr UnitTypeLhs& operator/=(UnitTypeLhs& lhs, const D& rhs)
4147 {
4148 return (lhs /= rhs.value());
4149 }
4150
4151 template<RatioDimensionlessUnitType U, RatioDimensionlessUnitType URhs>
4152 requires(traits::has_linear_scale_v<U, URhs>)
4153 constexpr U& operator/=(U& lhs, const URhs& rhs) noexcept
4154 {
4155 using Under0 = std::common_type_t<typename U::underlying_type, typename URhs::underlying_type>;
4156 using Under = detail::floating_point_promotion_t<Under0>;
4157
4158 const Under rhs_frac = static_cast<Under>(rhs.value()); // normalized fraction
4159
4160 const Under new_points = static_cast<Under>(lhs.raw()) / rhs_frac;
4161
4162 lhs = U(new_points);
4163 return lhs;
4164 }
4165
4166 template<RatioDimensionlessUnitType U>
4167 requires(units::traits::has_linear_scale_v<U>)
4168 constexpr U& operator/=(U& lhs, const U& rhs) noexcept
4169 {
4170 using Underlying = typename U::underlying_type;
4171 using R = typename U::conversion_factor::conversion_ratio;
4172
4173 constexpr long double points_per_one = static_cast<long double>(R::den) / static_cast<long double>(R::num);
4174
4175 const long double lhs_frac = static_cast<long double>(lhs.value());
4176 const long double rhs_frac = static_cast<long double>(rhs.value());
4177
4178 const long double out_frac = lhs_frac / rhs_frac;
4179 const long double out_points = out_frac * points_per_one;
4180
4181 if constexpr (std::is_integral_v<Underlying>)
4182 {
4183 lhs = U(static_cast<Underlying>(std::llround(out_points)));
4184 }
4185 else
4186 {
4187 lhs = U(static_cast<Underlying>(out_points));
4188 }
4189 return lhs;
4190 }
4191
4192 template<RatioDimensionlessUnitType U, units::ArithmeticType T>
4193 requires(units::traits::has_linear_scale_v<U>)
4194 constexpr U& operator/=(U& lhs, T rhs) noexcept
4195 {
4196 using Underlying = typename U::underlying_type;
4197 using R = typename U::conversion_factor::conversion_ratio;
4198
4199 constexpr long double points_per_one = static_cast<long double>(R::den) / static_cast<long double>(R::num);
4200
4201 const long double lhs_frac = static_cast<long double>(lhs.value());
4202 const long double out_frac = lhs_frac / static_cast<long double>(rhs);
4203 const long double out_pts = out_frac * points_per_one;
4204
4205 if constexpr (std::is_integral_v<Underlying>)
4206 {
4207 lhs = U(static_cast<Underlying>(std::llround(out_pts)));
4208 }
4209 else
4210 {
4211 lhs = U(static_cast<Underlying>(out_pts));
4212 }
4213 return lhs;
4214 }
4215
4216 template<RatioDimensionlessUnitType U, DimensionlessUnitType D>
4217 requires(units::traits::has_linear_scale_v<U, D> && !RatioDimensionlessUnitType<D>)
4218 constexpr U& operator/=(U& lhs, const D& rhs) noexcept
4219 {
4220 return (lhs /= rhs.value());
4221 }
4222
4223 template<DimensionedUnitType UnitTypeLhs>
4225 constexpr UnitTypeLhs& operator%=(UnitTypeLhs& lhs, const detail::type_identity_t<UnitTypeLhs>& rhs) noexcept
4226 {
4227 lhs = lhs % rhs;
4228 return lhs;
4229 }
4230
4231 template<DimensionlessUnitType UnitTypeLhs, DimensionlessUnitType UnitTypeRhs>
4233 constexpr UnitTypeLhs& operator%=(UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4234 {
4235 using CommonUnit = decltype(lhs % rhs);
4236 lhs = CommonUnit(lhs.raw() % rhs.raw());
4237 return lhs;
4238 }
4239
4240 template<UnitType UnitTypeLhs>
4242 constexpr UnitTypeLhs& operator%=(UnitTypeLhs& lhs, const typename UnitTypeLhs::underlying_type& rhs) noexcept
4243 {
4244 lhs = lhs % rhs;
4245 return lhs;
4246 }
4247
4248 // ratio-dimensionless %= ratio-dimensionless (percent points modulo percent points)
4249 template<RatioDimensionlessUnitType U>
4250 requires(traits::has_linear_scale_v<U>)
4251 constexpr U& operator%=(U& lhs, const U& rhs) noexcept
4252 {
4253 lhs = lhs % rhs;
4254 return lhs;
4255 }
4256
4257 // ratio-dimensionless %= scalar (percent points modulo scalar)
4258 template<RatioDimensionlessUnitType U>
4259 requires(traits::has_linear_scale_v<U>)
4260 constexpr U& operator%=(U& lhs, const typename U::underlying_type& rhs) noexcept
4261 {
4262 lhs = lhs % rhs;
4263 return lhs;
4264 }
4265
4266 // ratio-dimensionless %= base dimensionless unit (treat as scalar)
4267 template<RatioDimensionlessUnitType U, DimensionlessUnitType D>
4268 requires(traits::has_linear_scale_v<U, D> && !RatioDimensionlessUnitType<D>)
4269 constexpr U& operator%=(U& lhs, const D& rhs) noexcept
4270 {
4271 // D is ordinary dimensionless: safe scalar conversion
4272 lhs = lhs % static_cast<typename U::underlying_type>(rhs);
4273 return lhs;
4274 }
4275
4276 //------------------------------
4277 // UNIT UNARY OPERATORS
4278 //------------------------------
4279
4280 // unary addition: +T
4281 template<UnitType UnitTypeLhs>
4282 constexpr UnitTypeLhs operator+(const UnitTypeLhs& u) noexcept
4283 {
4284 return u;
4285 }
4286
4287 // prefix increment: ++T
4288 template<UnitType UnitTypeLhs>
4289 constexpr UnitTypeLhs& operator++(UnitTypeLhs& u) noexcept
4290 {
4291 u = UnitTypeLhs(u.raw() + 1);
4292 return u;
4293 }
4294
4295 // postfix increment: T++
4296 template<UnitType UnitTypeLhs>
4297 constexpr UnitTypeLhs operator++(UnitTypeLhs& u, int) noexcept
4298 {
4299 auto ret = u;
4300 u = UnitTypeLhs(u.raw() + 1);
4301 return ret;
4302 }
4303
4304 // unary addition: -T
4305 template<UnitType UnitTypeLhs>
4306 constexpr UnitTypeLhs operator-(const UnitTypeLhs& u) noexcept
4307 {
4308 return UnitTypeLhs(-u.raw());
4309 }
4310
4311 // prefix increment: --T
4312 template<UnitType UnitTypeLhs>
4313 constexpr UnitTypeLhs& operator--(UnitTypeLhs& u) noexcept
4314 {
4315 u = UnitTypeLhs(u.raw() - 1);
4316 return u;
4317 }
4318
4319 // postfix increment: T--
4320 template<UnitType UnitTypeLhs>
4321 constexpr UnitTypeLhs operator--(UnitTypeLhs& u, int) noexcept
4322 {
4323 auto ret = u;
4324 u = UnitTypeLhs(u.raw() - 1);
4325 return ret;
4326 }
4327
4328 //------------------------------
4329 // LINEAR ARITHMETIC
4330 //------------------------------
4331
4337 /// physically intended operation.
4338 /// @details The result is expressed in the LEFT operand's unit, so the caller controls the result unit by
4339 /// operand order (`meters + feet` is meters, `feet + meters` is feet) and the value reads in the unit
4340 /// they named. The underlying is widened only when the left operand's is integral and the right cannot
4341 /// convert into it without truncation, in which case the result reconciles to the common (finest,
4342 /// lossless) unit — the same exact behavior integer comparisons rely on.
4343 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
4344 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> &&
4346 constexpr auto operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4347 {
4348 // The result unit is computed in the body (not the signature) so the trait is never instantiated for a
4349 // non-unit operand that the constraint above already rejects — a stricter compiler evaluates a trailing
4350 // return type during overload resolution and would otherwise hard-error on, e.g., a vector iterator's
4351 // pointer subtraction that briefly considers this operator.
4352 using ResultUnit = detail::lhs_result_unit_t<UnitTypeLhs, UnitTypeRhs>;
4353 return ResultUnit(ResultUnit(lhs).raw() + ResultUnit(rhs).raw());
4354 }
4355
4357 template<RatioDimensionlessUnitType U, ArithmeticType T>
4358 requires(traits::has_linear_scale_v<U>)
4359 constexpr auto operator+(const U& lhs, T rhs) noexcept -> traits::replace_underlying_t<U, detail::floating_point_promotion_t<std::common_type_t<typename U::underlying_type, T>>>
4360 {
4361 using Under0 = std::common_type_t<typename U::underlying_type, T>;
4362 using Under = detail::floating_point_promotion_t<Under0>;
4363 using Ret = traits::replace_underlying_t<U, Under>;
4364
4365 using R = typename U::conversion_factor::conversion_ratio; // e.g. percent: 1/100
4366 constexpr Under points_per_one = static_cast<Under>(R::den) / static_cast<Under>(R::num);
4367
4368 // fraction-space math, then back to points
4369 const Under frac = static_cast<Under>(lhs.value()) + static_cast<Under>(rhs);
4370 return Ret(frac * points_per_one);
4371 }
4372
4373 template<RatioDimensionlessUnitType U, ArithmeticType T>
4374 requires(traits::has_linear_scale_v<U>)
4375 constexpr auto operator+(T lhs, const U& rhs) noexcept -> traits::replace_underlying_t<U, detail::floating_point_promotion_t<std::common_type_t<T, typename U::underlying_type>>>
4376 {
4377 using Under0 = std::common_type_t<T, typename U::underlying_type>;
4378 using Under = detail::floating_point_promotion_t<Under0>;
4379 using Ret = traits::replace_underlying_t<U, Under>;
4380
4381 using R = typename U::conversion_factor::conversion_ratio;
4382 constexpr Under points_per_one = static_cast<Under>(R::den) / static_cast<Under>(R::num);
4384 const Under frac = static_cast<Under>(lhs) + static_cast<Under>(rhs.value());
4385 return Ret(frac * points_per_one);
4386 }
4387
4388
4390 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4391 requires(traits::has_linear_scale_v<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeLhs>)
4392 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>> operator+(const UnitTypeLhs& lhs, T rhs) noexcept
4394 using ret = traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>>;
4395 return ret(lhs.raw() + static_cast<ret::underlying_type>(rhs));
4396 }
4397
4400 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4401 requires(traits::has_linear_scale_v<UnitTypeRhs> && !RatioDimensionlessUnitType<UnitTypeRhs>)
4402 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<T, typename UnitTypeRhs::underlying_type>> operator+(T lhs, const UnitTypeRhs& rhs) noexcept
4403 {
4404 // Apply any necessary scale factor to T using multiplication for lossless conversion
4405 // for non-scaled dimensionless units it's a no-op
4406 using CommonUnit = decltype(lhs + rhs);
4407 using InverseCommonUnit = decltype(1 / CommonUnit(1));
4408 return CommonUnit(InverseCommonUnit(lhs).value() + rhs.raw());
4410
4415 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
4416 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> &&
4418 constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4419 {
4420 // Result unit computed in the body, not the signature — see operator+ above.
4421 using ResultUnit = detail::lhs_result_unit_t<UnitTypeLhs, UnitTypeRhs>;
4422 return ResultUnit(ResultUnit(lhs).raw() - ResultUnit(rhs).raw());
4423 }
4424
4426 /// @details The difference of two absolute affine quantities is a DELTA: the datum offsets cancel, so
4427 /// the result must be a pure (non-affine) quantity — otherwise storing it back into an affine
4428 /// unit would re-apply the offset (e.g. celsius(0) - kelvin(0) would read 546.30 K instead of
4429 /// the true 273.15 K delta). Both operands are reconciled to their common affine unit, their
4430 /// raw values subtracted (the offsets cancel exactly), and the result returned in the
4431 /// offset-stripped counterpart of that common unit so it never re-applies a datum.
4432 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
4433 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> &&
4435 constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4436 {
4437 // Reconcile to the LEFT operand's affine unit (its datum applied to the right operand as it converts),
4438 // so the delta is expressed in the left operand's scale — celsius(100) - fahrenheit(32) is 100 celsius
4439 // degrees, not a value in an anonymous sub-unit of the two scales' common measure. The result is the
4440 // offset-STRIPPED counterpart of the left unit so no datum is ever re-applied to the delta.
4441 using LhsCf = typename traits::unit_traits<UnitTypeLhs>::conversion_factor;
4443 typename traits::conversion_factor_traits<LhsCf>::dimension_type,
4444 typename traits::conversion_factor_traits<LhsCf>::pi_exponent_ratio, std::ratio<0>>;
4445 using DeltaUnit = unit<traits::strong_t<DeltaCf>, typename UnitTypeLhs::underlying_type, typename UnitTypeLhs::numerical_scale_type>;
4446 return DeltaUnit(lhs.raw() - UnitTypeLhs(rhs).raw());
4447 }
4448
4451 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4452 requires(traits::has_linear_scale_v<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeLhs>)
4453 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>> operator-(const UnitTypeLhs& lhs, T rhs) noexcept
4454 {
4455 // Apply any necessary scale factor to T using multiplication for lossless conversion
4456 // for non-scaled dimensionless units it's a no-op
4457 using CommonUnit = decltype(lhs - rhs);
4458 using InverseCommonUnit = decltype(1 / CommonUnit(1));
4459 return CommonUnit(lhs.raw() - InverseCommonUnit(rhs).value());
4460 }
4461
4463 template<RatioDimensionlessUnitType U, ArithmeticType T>
4464 requires(traits::has_linear_scale_v<U>)
4465 constexpr auto operator-(const U& lhs, T rhs) noexcept -> traits::replace_underlying_t<U, detail::floating_point_promotion_t<std::common_type_t<typename U::underlying_type, T>>>
4466 {
4467 using Under0 = std::common_type_t<typename U::underlying_type, T>;
4468 using Under = detail::floating_point_promotion_t<Under0>;
4469 using Ret = traits::replace_underlying_t<U, Under>;
4470
4471 using R = typename U::conversion_factor::conversion_ratio;
4472 constexpr Under points_per_one = static_cast<Under>(R::den) / static_cast<Under>(R::num);
4473
4474 const Under frac = static_cast<Under>(lhs.value()) - static_cast<Under>(rhs);
4475 return Ret(frac * points_per_one);
4476 }
4477
4478 template<RatioDimensionlessUnitType U, ArithmeticType T>
4479 requires(traits::has_linear_scale_v<U>)
4480 constexpr auto operator-(T lhs, const U& rhs) noexcept -> traits::replace_underlying_t<U, detail::floating_point_promotion_t<std::common_type_t<T, typename U::underlying_type>>>
4481 {
4482 using Under0 = std::common_type_t<T, typename U::underlying_type>;
4483 using Under = detail::floating_point_promotion_t<Under0>;
4484 using Ret = traits::replace_underlying_t<U, Under>;
4485
4486 using R = typename U::conversion_factor::conversion_ratio;
4487 constexpr Under points_per_one = static_cast<Under>(R::den) / static_cast<Under>(R::num);
4489 const Under frac = static_cast<Under>(lhs) - static_cast<Under>(rhs.value());
4490 return Ret(frac * points_per_one);
4491 }
4492
4495 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4496 requires(traits::has_linear_scale_v<UnitTypeRhs> && !RatioDimensionlessUnitType<UnitTypeRhs>)
4497 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<T, typename UnitTypeRhs::underlying_type>> operator-(T lhs, const UnitTypeRhs& rhs) noexcept
4498 {
4499 // Apply any necessary scale factor to T using multiplication for lossless conversion
4500 // for non-scaled dimensionless units it's a no-op
4501 using CommonUnit = decltype(lhs - rhs);
4502 using InverseCommonUnit = decltype(1 / CommonUnit(1));
4503 return CommonUnit(InverseCommonUnit(lhs).value() - rhs.raw());
4504 }
4505
4508 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
4509 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4510 constexpr auto operator*(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4511 -> detail::rewrap_to_named_t<unit<traits::strong_t<squared<typename traits::unit_traits<std::common_type_t<UnitTypeLhs, UnitTypeRhs>>::conversion_factor>>,
4512 typename std::common_type_t<UnitTypeLhs, UnitTypeRhs>::underlying_type>>
4513 {
4514 using SquaredUnit = decltype(lhs * rhs);
4515 using CommonUnit = std::common_type_t<UnitTypeLhs, UnitTypeRhs>;
4516 return SquaredUnit(CommonUnit(lhs).raw() * CommonUnit(rhs).raw());
4517 }
4518
4521 template<DimensionedUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4522 requires(!same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4523 constexpr auto operator*(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4524 -> detail::rewrap_to_named_t<unit<traits::strong_t<compound_conversion_factor<typename traits::unit_traits<UnitTypeLhs>::conversion_factor, typename traits::unit_traits<UnitTypeRhs>::conversion_factor>>,
4525 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>>>
4527 using CompoundUnit = decltype(lhs * rhs);
4528 using CommonUnderlying = typename CompoundUnit::underlying_type;
4529 return CompoundUnit(static_cast<CommonUnderlying>(lhs) * static_cast<CommonUnderlying>(rhs));
4530 }
4531
4532
4533 template<DimensionedUnitType UnitTypeLhs, OrdinaryDimensionlessUnitType UnitTypeRhs>
4534 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4535 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator*(
4536 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4538 using CommonUnit = decltype(lhs * rhs);
4539 return CommonUnit(CommonUnit(lhs).raw() * static_cast<typename CommonUnit::underlying_type>(rhs));
4540 }
4541
4544 template<DimensionedUnitType UnitTypeLhs, RatioDimensionlessUnitType UnitTypeRhs>
4545 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4546 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator*(
4547 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4548 {
4549 using Out = decltype(lhs * rhs);
4550 using U0 = std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>;
4551 using U = detail::floating_point_promotion_t<U0>;
4552
4553 // rhs.value() is normalized fraction (e.g. 200_pct -> 2.0, 50_ppb -> 50e-9)
4554 return Out(static_cast<U>(lhs.raw()) * static_cast<U>(rhs.value()));
4555 }
4556
4557
4558 template<OrdinaryDimensionlessUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4559 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4560 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator*(
4561 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4562 {
4563 using CommonUnit = decltype(lhs * rhs);
4564 return CommonUnit(static_cast<typename CommonUnit::underlying_type>(lhs) * CommonUnit(rhs).raw());
4565 }
4566
4568 template<RatioDimensionlessUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4569 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4570 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator*(
4571 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4572 {
4573 using Out = decltype(lhs * rhs);
4574 using U0 = std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>;
4575 using U = detail::floating_point_promotion_t<U0>;
4576
4577 return Out(static_cast<U>(lhs.value()) * static_cast<U>(rhs.raw()));
4578 }
4579
4581 template<DimensionedUnitType UnitTypeLhs, ArithmeticType T>
4582 requires(traits::has_linear_scale_v<UnitTypeLhs>)
4583 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>> operator*(const UnitTypeLhs& lhs, T rhs) noexcept
4584 {
4585 using CommonUnit = decltype(lhs * rhs);
4586 return CommonUnit(CommonUnit(lhs).raw() * rhs);
4587 }
4588
4590 template<DimensionedUnitType UnitTypeRhs, ArithmeticType T>
4591 requires(traits::has_linear_scale_v<UnitTypeRhs>)
4592 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<T, typename UnitTypeRhs::underlying_type>> operator*(T lhs, const UnitTypeRhs& rhs) noexcept
4593 {
4594 using CommonUnit = decltype(lhs * rhs);
4595 return CommonUnit(lhs * CommonUnit(rhs).raw());
4596 }
4597
4599 template<RatioDimensionlessUnitType U, units::ArithmeticType T>
4600 requires(units::traits::has_linear_scale_v<U>)
4601 constexpr units::dimensionless<units::detail::floating_point_promotion_t<std::common_type_t<T, typename U::underlying_type>>> operator*(T lhs, const U& rhs) noexcept
4602 {
4603 using Under0 = std::common_type_t<T, typename U::underlying_type>;
4604 using Under = units::detail::floating_point_promotion_t<Under0>;
4605
4606 // rhs converts to Under as normalized fraction (e.g. 50_pct -> 0.5)
4607 return units::dimensionless<Under>(static_cast<Under>(lhs) * static_cast<Under>(rhs));
4608 }
4609
4611 template<RatioDimensionlessUnitType U, units::ArithmeticType T>
4612 requires(units::traits::has_linear_scale_v<U>)
4613 constexpr units::dimensionless<units::detail::floating_point_promotion_t<std::common_type_t<typename U::underlying_type, T>>> operator*(const U& lhs, T rhs) noexcept
4614 {
4615 using Under0 = std::common_type_t<typename U::underlying_type, T>;
4616 using Under = units::detail::floating_point_promotion_t<Under0>;
4617
4618 return units::dimensionless<Under>(static_cast<Under>(lhs) * static_cast<Under>(rhs));
4619 }
4620
4622 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4623 requires(traits::has_linear_scale_v<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeLhs>)
4624 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>> operator*(const UnitTypeLhs& lhs, T rhs) noexcept
4625 {
4626 using CommonUnit = decltype(lhs * rhs);
4627 return CommonUnit(lhs.raw() * rhs);
4628 }
4629
4631 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4632 requires(traits::has_linear_scale_v<UnitTypeRhs> && !RatioDimensionlessUnitType<UnitTypeRhs>)
4633 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<T, typename UnitTypeRhs::underlying_type>> operator*(T lhs, const UnitTypeRhs& rhs) noexcept
4634 {
4635 using CommonUnit = decltype(lhs * rhs);
4636 return CommonUnit(lhs * rhs.raw());
4637 }
4638
4640
4641 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
4642 requires(
4643 same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> && !RatioDimensionlessUnitType<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeRhs>)
4644 constexpr dimensionless<std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4646 using CommonUnit = std::common_type_t<UnitTypeLhs, UnitTypeRhs>;
4647 return CommonUnit(lhs).raw() / CommonUnit(rhs).raw();
4648 }
4649
4652 template<DimensionedUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4653 requires(!same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4654 constexpr auto operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4655 -> detail::rewrap_to_named_t<unit<traits::strong_t<compound_conversion_factor<typename traits::unit_traits<UnitTypeLhs>::conversion_factor, inverse<typename traits::unit_traits<UnitTypeRhs>::conversion_factor>>>,
4656 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>>>
4658 using CompoundUnit = decltype(lhs / rhs);
4659 using CommonUnderlying = typename CompoundUnit::underlying_type;
4660 return CompoundUnit(static_cast<CommonUnderlying>(lhs) / static_cast<CommonUnderlying>(rhs));
4661 }
4662
4664 template<DimensionedUnitType UnitTypeLhs, OrdinaryDimensionlessUnitType UnitTypeRhs>
4665 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4666 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator/(
4667 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4668 {
4669 using CommonUnit = decltype(lhs / rhs);
4670 using CommonUnderlying = typename CommonUnit::underlying_type;
4671
4672 // Ordinary dimensionless is a true scalar
4673 return CommonUnit(CommonUnit(lhs).raw() / static_cast<CommonUnderlying>(rhs));
4674 }
4678 template<DimensionedUnitType UnitTypeLhs, RatioDimensionlessUnitType UnitTypeRhs>
4679 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4680 constexpr traits::replace_underlying_t<
4681 UnitTypeLhs,
4682 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>
4683 >
4684 operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4685 {
4686 using Out = decltype(lhs / rhs);
4687 using U0 = std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>;
4688 using U = detail::floating_point_promotion_t<U0>;
4689
4690 return Out(static_cast<U>(lhs.raw()) / static_cast<U>(rhs.value()));
4691 }
4692
4695 template<OrdinaryDimensionlessUnitType UnitTypeLhs, RatioDimensionlessUnitType UnitTypeRhs>
4696 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4697 constexpr auto operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> detail::rewrap_to_named_t<unit<traits::strong_t<inverse<typename traits::unit_traits<UnitTypeRhs>::conversion_factor>>,
4698 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>>>
4699 {
4700 using Out = decltype(lhs / rhs);
4701 using CommonUnderlying = typename Out::underlying_type;
4702
4703 // lhs is true scalar, rhs is points (not scalar fraction)
4704 return Out(static_cast<CommonUnderlying>(lhs) / static_cast<CommonUnderlying>(rhs.raw()));
4705 }
4706
4708 template<OrdinaryDimensionlessUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4709 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> && traits::is_dimensionless_unit_v<UnitTypeLhs>)
4710 constexpr auto operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> detail::rewrap_to_named_t<unit<traits::strong_t<inverse<typename traits::unit_traits<UnitTypeRhs>::conversion_factor>>,
4711 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>>>
4712 {
4713 using CommonUnit = decltype(lhs / rhs);
4714 using CommonUnderlying = typename CommonUnit::underlying_type;
4715 return CommonUnit(static_cast<CommonUnderlying>(lhs) / static_cast<CommonUnderlying>(rhs));
4716 }
4717
4720 template<RatioDimensionlessUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4721 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4722 constexpr auto operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4723 -> unit<
4724 traits::strong_t<
4726 typename traits::unit_traits<UnitTypeLhs>::conversion_factor,
4727 inverse<typename traits::unit_traits<UnitTypeRhs>::conversion_factor>
4728 >
4729 >,
4730 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>
4731 >
4732 {
4733 using Out = decltype(lhs / rhs);
4734 using CommonUnderlying = typename Out::underlying_type;
4735
4736 // numeric part: ppb points / years -> "ppb per year" numeric value
4737 // keep lhs as points (raw), keep rhs in its own units (raw)
4738 return Out(
4739 static_cast<CommonUnderlying>(lhs.raw()) / static_cast<CommonUnderlying>(rhs.raw()),
4740 linearized_value
4741 );
4742 }
4743
4745 template<UnitType UnitTypeLhs, ArithmeticType T>
4746 requires(traits::has_linear_scale_v<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeLhs>)
4747 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>> operator/(const UnitTypeLhs& lhs, T rhs) noexcept
4748 {
4749 using CommonUnit = decltype(lhs / rhs);
4750 return CommonUnit(CommonUnit(lhs).raw() / rhs);
4751 }
4752
4754 template<UnitType UnitTypeRhs, ArithmeticType T>
4755 requires(traits::has_linear_scale_v<UnitTypeRhs> && !RatioDimensionlessUnitType<UnitTypeRhs>)
4756 constexpr auto operator/(T lhs, const UnitTypeRhs& rhs) noexcept
4757 -> detail::rewrap_to_named_t<unit<traits::strong_t<inverse<typename traits::unit_traits<UnitTypeRhs>::conversion_factor>>, std::common_type_t<T, typename UnitTypeRhs::underlying_type>>>
4758 {
4759 using InverseUnit = decltype(lhs / rhs);
4760 using UnitConversion = typename traits::unit_traits<UnitTypeRhs>::conversion_factor;
4761 using CommonUnderlying = std::common_type_t<T, typename UnitTypeRhs::underlying_type>;
4762 using CommonUnit = unit<UnitConversion, CommonUnderlying>;
4763 return InverseUnit(lhs / CommonUnit(rhs).raw());
4764 }
4765
4766
4767 // U / scalar -> U (percent points divided, still percent)
4768 template<RatioDimensionlessUnitType U, ArithmeticType T>
4769 requires(traits::has_linear_scale_v<U>)
4770 constexpr traits::replace_underlying_t<U, std::common_type_t<typename U::underlying_type, T>> operator/(const U& lhs, T rhs) noexcept
4771 {
4772 using Out = traits::replace_underlying_t<U, std::common_type_t<typename U::underlying_type, T>>;
4773 return Out(Out(lhs).raw() / rhs);
4774 }
4775
4776 // scalar / ratio-dimensionless -> dimensionless (normalized)
4777 template<RatioDimensionlessUnitType U, ArithmeticType T>
4778 requires(traits::has_linear_scale_v<U>)
4779 constexpr units::dimensionless<detail::floating_point_promotion_t<std::common_type_t<T, typename U::underlying_type>>> operator/(T lhs, const U& rhs) noexcept
4780 {
4781 using CommonType = std::common_type_t<T, typename U::underlying_type>;
4782 using PromotedType = detail::floating_point_promotion_t<CommonType>;
4783
4784 // rhs.value() is normalized fraction (e.g. 50_pct -> 0.5)
4785 return units::dimensionless<PromotedType>(static_cast<PromotedType>(lhs) / static_cast<PromotedType>(rhs.value()));
4786 }
4787
4788 // U / U -> dimensionless (normalized)
4789 template<RatioDimensionlessUnitType U1, RatioDimensionlessUnitType U2>
4790 requires(traits::has_linear_scale_v<U1, U2>)
4791 constexpr dimensionless<detail::floating_point_promotion_t<std::common_type_t<typename U1::underlying_type, typename U2::underlying_type>>> operator/(const U1& lhs, const U2& rhs) noexcept
4792 {
4793 using Under0 = std::common_type_t<typename U1::underlying_type, typename U2::underlying_type>;
4794 using Under = detail::floating_point_promotion_t<Under0>;
4795 return dimensionless<Under>(static_cast<Under>(lhs.value()) / static_cast<Under>(rhs.value()));
4796 }
4797
4798 /// Modulo for convertible unit types with a linear scale. @returns the lhs value modulo the rhs value, in
4799 /// their common (finer) unit.
4800 /// @note The result is the `std::common_type` of the operands — the finer of the two units — not the
4801 /// lhs unit. Returning the lhs unit made the operator order-dependent: `meters % kilometers`
4802 /// compiled (finer lhs) but `kilometers % meters` did not (converting the finer common result
4803 /// back to the coarser lhs is lossy for an integer underlying, disabling the constructor). The
4804 /// common-unit result mirrors `fmod` and removes the asymmetry.
4805 template<DimensionedUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4806 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4807 constexpr std::common_type_t<UnitTypeLhs, UnitTypeRhs> operator%(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4808 {
4809 using CommonUnit = std::common_type_t<UnitTypeLhs, UnitTypeRhs>;
4810 return CommonUnit(CommonUnit(lhs).raw() % CommonUnit(rhs).raw());
4811 }
4812
4814 template<DimensionedUnitType UnitTypeLhs, DimensionlessUnitType UnitTypeRhs>
4815 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4816 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator%(
4817 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4818 {
4819 using CommonUnit = decltype(lhs % rhs);
4820 using CommonUnderlying = typename CommonUnit::underlying_type;
4821 return CommonUnit(CommonUnit(lhs).raw() % static_cast<CommonUnderlying>(rhs));
4822 }
4823
4824
4826 template<DimensionlessUnitType UnitTypeLhs, DimensionlessUnitType UnitTypeRhs>
4827 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4828 constexpr traits::replace_underlying_t<UnitTypeLhs, typename std::common_type_t<UnitTypeLhs, UnitTypeRhs>::underlying_type> operator%(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4829 {
4830 using CommonUnit = decltype(lhs % rhs);
4831 return CommonUnit(lhs.raw() % rhs.raw());
4832 }
4833
4835 template<UnitType UnitTypeLhs, ArithmeticType T>
4836 requires(traits::has_linear_scale_v<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeLhs>)
4837 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>> operator%(const UnitTypeLhs& lhs, const T& rhs) noexcept
4838 {
4839 using CommonUnit = decltype(lhs % rhs);
4840 return CommonUnit(CommonUnit(lhs).raw() % rhs);
4841 }
4842
4843 // Modulos for ratio-like dimensionless units
4844 template<RatioDimensionlessUnitType U>
4845 requires(traits::has_linear_scale_v<U>)
4846 constexpr U operator%(const U& lhs, const U& rhs) noexcept
4847 {
4848 return U(lhs.raw() % rhs.raw());
4849 }
4850
4851 template<RatioDimensionlessUnitType U, ArithmeticType T>
4852 requires(traits::has_linear_scale_v<U>)
4853 constexpr U operator%(const U& lhs, T rhs) noexcept
4854 {
4855 return U(lhs.raw() % rhs);
4856 }
4857
4858 template<RatioDimensionlessUnitType U, ArithmeticType T>
4859 requires(traits::has_linear_scale_v<U>)
4860 constexpr U operator%(T lhs, const U& rhs) noexcept
4861 {
4862 using Under = detail::floating_point_promotion_t<std::common_type_t<T, typename U::underlying_type>>;
4863 // If lhs is integral, keep integer modulo semantics
4864 if constexpr (std::is_integral_v<T> && std::is_integral_v<typename U::underlying_type>)
4865 return U(lhs % rhs.raw());
4866 else
4867 return U(static_cast<Under>(std::fmod(static_cast<Under>(lhs), static_cast<Under>(rhs.raw()))));
4868 }
4869
4870 //----------------------------------
4871 // DIMENSIONLESS COMPARISONS
4872 //----------------------------------
4873
4874 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4875 constexpr bool operator==(const T& lhs, const UnitTypeRhs& rhs) noexcept
4876 {
4877 using CommonUnderlying = std::common_type_t<T, typename UnitTypeRhs::underlying_type>;
4878
4879 const auto common_lhs = static_cast<CommonUnderlying>(lhs);
4880 const auto common_rhs = static_cast<CommonUnderlying>(rhs);
4881
4882 if constexpr (std::is_integral_v<CommonUnderlying>)
4883 {
4884 return common_lhs == common_rhs;
4885 }
4886 else
4887 {
4888 return abs(common_lhs - common_rhs) < std::numeric_limits<CommonUnderlying>::epsilon() * abs(common_lhs + common_rhs) ||
4889 abs(common_lhs - common_rhs) < std::numeric_limits<CommonUnderlying>::min();
4890 }
4891 }
4892
4893 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4894 constexpr bool operator==(const UnitTypeLhs& lhs, const T& rhs) noexcept
4895 {
4896 return rhs == lhs;
4897 }
4898
4899 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4900 requires(traits::is_dimensionless_unit_v<UnitTypeRhs> && std::is_arithmetic_v<T>)
4901 constexpr bool operator!=(const T& lhs, const UnitTypeRhs& rhs) noexcept
4902 {
4903 return !(lhs == rhs);
4904 }
4905
4906 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4907 constexpr bool operator!=(const UnitTypeLhs& lhs, const T& rhs) noexcept
4908 {
4909 return !(lhs == rhs);
4910 }
4911
4912 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4913 requires(traits::is_dimensionless_unit_v<UnitTypeRhs> && std::is_arithmetic_v<T>)
4914 constexpr bool operator>=(const T& lhs, const UnitTypeRhs& rhs) noexcept
4915 {
4916 using CommonUnderlying = std::common_type_t<T, typename UnitTypeRhs::underlying_type>;
4917 return lhs >= static_cast<CommonUnderlying>(rhs);
4918 }
4919
4920 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4921 constexpr bool operator>=(const UnitTypeLhs& lhs, const T& rhs) noexcept
4922 {
4923 using CommonUnderlying = std::common_type_t<typename UnitTypeLhs::underlying_type, T>;
4924 return static_cast<CommonUnderlying>(lhs) >= rhs;
4925 }
4926
4927 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4928 constexpr bool operator>(const T& lhs, const UnitTypeRhs& rhs) noexcept
4929 {
4930 using CommonUnderlying = std::common_type_t<T, typename UnitTypeRhs::underlying_type>;
4931 return lhs > static_cast<CommonUnderlying>(rhs);
4932 }
4933
4934 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4935 constexpr bool operator>(const UnitTypeLhs& lhs, const T& rhs) noexcept
4936 {
4937 using CommonUnderlying = std::common_type_t<typename UnitTypeLhs::underlying_type, T>;
4938 return static_cast<CommonUnderlying>(lhs) > rhs;
4939 }
4940
4941 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4942 constexpr bool operator<=(const T& lhs, const UnitTypeRhs& rhs) noexcept
4943 {
4944 using CommonUnderlying = std::common_type_t<T, typename UnitTypeRhs::underlying_type>;
4945 return lhs <= static_cast<CommonUnderlying>(rhs);
4946 }
4947
4948 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4949 constexpr bool operator<=(const UnitTypeLhs& lhs, const T& rhs) noexcept
4950 {
4951 using CommonUnderlying = std::common_type_t<typename UnitTypeLhs::underlying_type, T>;
4952 return static_cast<CommonUnderlying>(lhs) <= rhs;
4953 }
4954
4955 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4956 constexpr bool operator<(const T& lhs, const UnitTypeRhs& rhs) noexcept
4957 {
4958 using CommonUnderlying = std::common_type_t<T, typename UnitTypeRhs::underlying_type>;
4959 return lhs < static_cast<CommonUnderlying>(rhs);
4960 }
4961
4962 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4963 constexpr bool operator<(const UnitTypeLhs& lhs, const T& rhs) noexcept
4964 {
4965 using CommonUnderlying = std::common_type_t<typename UnitTypeLhs::underlying_type, T>;
4966 return static_cast<CommonUnderlying>(lhs) < rhs;
4967 }
4968
4969 //----------------------------------
4970 // POW
4971 //----------------------------------
4972 // DOXYGEN IGNORE
4974 namespace detail
4975 {
4977 template<int N, class U>
4978 struct power_of_unit
4979 {
4980 template<bool isPos, int V>
4981 struct power_of_unit_impl;
4982
4983 template<int V>
4984 struct power_of_unit_impl<true, V>
4985 {
4986 typedef unit_multiply<U, typename power_of_unit<N - 1, U>::type> type;
4987 };
4988
4989 template<int V>
4990 struct power_of_unit_impl<false, V>
4991 {
4992 typedef inverse<typename power_of_unit<-N, U>::type> type;
4993 };
4994
4995 typedef typename power_of_unit_impl<(N > 0), N>::type type;
4996 };
4997
4999 template<class U>
5000 struct power_of_unit<1, U>
5001 {
5002 typedef U type;
5003 };
5004
5005 template<class U>
5006 struct power_of_unit<0, U>
5007 {
5008 typedef dimensionless_ type;
5009 };
5010 } // namespace detail // END DOXYGEN IGNORE
5012
5014 * @brief computes the value of <i>value</i> raised to the <i>power</i>
5015 * @details Only implemented for linear_scale units. <i>Power</i> must be known at compile time, so the
5016 * resulting unit type can be deduced.
5017 * @tparam power exponential power to raise <i>value</i> by.
5018 * @param[in] value `unit` derived type to raise to the given <i>power</i>
5019 * @returns new unit, raised to the given exponent
5020 */
5021 template<int power, UnitType UnitType>
5022 requires(traits::has_linear_scale_v<UnitType>)
5023 constexpr auto pow(const UnitType& value) noexcept -> detail::rewrap_to_named_t<unit<traits::strong_t<typename units::detail::power_of_unit<power, typename units::traits::unit_traits<UnitType>::conversion_factor>::type>,
5024 detail::floating_point_promotion_t<typename units::traits::unit_traits<UnitType>::underlying_type>, linear_scale>>
5025 {
5026 return decltype(units::pow<power>(value))(pow<power>(value.raw()));
5027 }
5028
5029 //------------------------------
5030 // DECIBEL SCALE
5031 //------------------------------
5032
5039 {
5046 template<class T>
5047 static T linearize(const T value) noexcept
5048 {
5049 // A decibel value is stored through a base-10 logarithm, so an integral underlying type cannot
5050 // represent it: most decibel figures round to a wrong integer (3 dB stores as 0) and large ones
5051 // overflow. Asserted here, at the point a value is actually stored, so merely naming a
5052 // decibel-scale type for trait/overload resolution does not trip it.
5053 static_assert(std::is_floating_point_v<T>,
5054 "a decibel-scale unit requires a floating-point underlying type (an integral type cannot represent a logarithmic value)");
5055 return static_cast<T>(std::pow(10, value / 10));
5057
5060
5061
5064 template<class T>
5065 static T scale(const T value) noexcept
5066 {
5067 return static_cast<T>(10 * std::log10(value));
5068 }
5069 };
5071 //------------------------------
5072 // dimensionless (DECIBEL) UNITS
5073 //------------------------------
5074
5080#if !defined(UNIT_LIB_DISABLE_IOSTREAM)
5081 template<class Underlying>
5082 std::ostream& operator<<(std::ostream& os, const decibels<Underlying>& obj)
5083 {
5084 os << obj.raw() << " dB";
5085 return os;
5086 }
5087#endif
5088 template<class Underlying>
5089 using dBi = decibels<Underlying>;
5090
5091 // Register the name/abbreviation for the dimensionless decibel and its `_dB` literal. The reverse
5092 // named-class map is keyed on (conversion_factor, scale); the (dimensionless, decibel_scale) key
5093 // belongs to `decibels` alone (the power dB units use the watts/milliwatts factors), so the mapping
5094 // is unambiguous and the member name()/abbreviation() resolve through it.
5095 template<class Underlying>
5096 struct unit_name<decibels<Underlying>>
5097 {
5098 static constexpr const char* value = "decibels";
5099 };
5100
5101 template<class Underlying>
5102 struct unit_abbreviation<decibels<Underlying>>
5103 {
5104 static constexpr const char* value = "dB";
5105 };
5106
5107 namespace detail
5108 {
5110 typename ::units::decibels<>::conversion_factor*, typename ::units::decibels<>::numerical_scale_type*);
5111 }
5112
5113#ifndef UNIT_NO_LITERAL_SUPPORT
5114 namespace literals
5115 {
5116 // only a floating-point literal: a decibel scale requires a floating-point underlying type
5117 constexpr decibels<double> operator""_dB(long double d) noexcept
5118 {
5119 return decibels<double>(static_cast<double>(d));
5120 }
5121 } // namespace literals
5122#endif
5123
5124 //------------------------------
5125 // DECIBEL ARITHMETIC
5126 //------------------------------
5127
5133 /// (two equal powers sum to +3 dB), not by adding their dB numbers.
5134 template<DimensionedUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
5135 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5136 auto operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept = delete;
5137
5138
5140 template<DimensionlessUnitType UnitTypeLhs, DimensionlessUnitType UnitTypeRhs>
5141 requires(traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5142 constexpr std::common_type_t<UnitTypeLhs, UnitTypeRhs> operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
5143 {
5144 using CommonUnit = std::common_type_t<UnitTypeLhs, UnitTypeRhs>;
5145 return CommonUnit(CommonUnit(lhs).to_linearized() * CommonUnit(rhs).to_linearized(), linearized_value);
5146 }
5147
5148
5149 template<DimensionedUnitType UnitTypeLhs, DimensionlessUnitType UnitTypeRhs>
5150 requires(traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5151 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator+(
5152 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
5153 {
5154 using CommonUnit = decltype(lhs + rhs);
5155 return CommonUnit(lhs.to_linearized() * rhs.to_linearized(), linearized_value);
5156 }
5157
5158
5159 template<DimensionlessUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
5160 requires(traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5161 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator+(
5162 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
5163 {
5164 using CommonUnit = decltype(lhs + rhs);
5165 return CommonUnit(lhs.to_linearized() * rhs.to_linearized(), linearized_value);
5166 }
5167
5169 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5170 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5171 constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> decibels<typename std::common_type_t<UnitTypeLhs, UnitTypeRhs>::underlying_type>
5172 {
5173 using Dimensionless = decltype(lhs - rhs);
5174 using CommonUnit = std::common_type_t<UnitTypeLhs, UnitTypeRhs>;
5175
5176 return Dimensionless(CommonUnit(lhs).to_linearized() / CommonUnit(rhs).to_linearized(), linearized_value);
5177 }
5178
5179
5180 template<DimensionedUnitType UnitTypeLhs, DimensionlessUnitType UnitTypeRhs>
5181 requires(traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5182 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator-(
5183 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
5184 {
5185 using CommonUnit = decltype(lhs - rhs);
5186 return CommonUnit(lhs.to_linearized() / rhs.to_linearized(), linearized_value);
5187 }
5188
5189
5190 template<DimensionlessUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
5191 requires(traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5192 constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> detail::rewrap_to_named_t<unit<traits::strong_t<inverse<typename traits::unit_traits<UnitTypeRhs>::conversion_factor>>,
5193 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>, decibel_scale>>
5194 {
5195 using InverseUnit = decltype(lhs - rhs);
5196 return InverseUnit(lhs.to_linearized() / rhs.to_linearized(), linearized_value);
5197 }
5198
5199 //----------------------------------
5200 // UNIT-ENABLED CMATH FUNCTIONS
5201 //----------------------------------
5202
5203 //----------------------------------
5204 // MIN/MAX FUNCTIONS
5205 //----------------------------------
5206
5207 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5209 constexpr std::common_type_t<UnitTypeLhs, UnitTypeRhs> min(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs)
5210 {
5211 using CommonUnit = decltype(units::min(lhs, rhs));
5212 return (lhs < rhs ? CommonUnit(lhs) : CommonUnit(rhs));
5213 }
5214
5215 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5217 constexpr std::common_type_t<UnitTypeLhs, UnitTypeRhs> max(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs)
5218 {
5219 using CommonUnit = decltype(units::max(lhs, rhs));
5220 return (lhs > rhs ? CommonUnit(lhs) : CommonUnit(rhs));
5221 }
5222
5223 //----------------------------------
5224 // TRANSCENDENTAL FUNCTIONS
5225 //----------------------------------
5226
5227 // it makes NO SENSE to put dimensioned units into a transcendental function, and if you think it does you are
5228 // demonstrably wrong. https://en.wikipedia.org/wiki/Transcendental_function#Dimensional_analysis
5229
5232 * @brief Compute exponential function
5233 * @details Returns the base-e exponential function of x, which is e raised to the power x: ex.
5234 * @param[in] x dimensionless value of the exponent.
5235 * @returns Exponential value of x.
5236 * If the magnitude of the result is too large to be represented by a value of the return type, the
5237 * function returns HUGE_VAL (or HUGE_VALF or HUGE_VALL) with the proper sign, and an overflow range
5238 * error occurs
5239 */
5240 template<DimensionlessUnitType UnitType>
5241 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> exp(const UnitType x) noexcept
5242 {
5243 return std::exp(x.value());
5244 }
5245
5247 * @ingroup UnitMath
5248 * @brief Compute natural logarithm
5249 * @details Returns the natural logarithm of x.
5250 * @param[in] x dimensionless value whose logarithm is calculated. If the argument is negative, a
5251 * domain error occurs.
5252 * @sa log10 for more common base-10 logarithms
5253 * @returns Natural logarithm of x.
5254 */
5255 template<DimensionlessUnitType UnitType>
5256 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> log(const UnitType x) noexcept
5257 {
5258 return std::log(x.value());
5259 }
5260
5261 /**
5262 * @ingroup UnitMath
5263 * @brief Compute common logarithm
5264 * @details Returns the common (base-10) logarithm of x.
5265 * @param[in] x Value whose logarithm is calculated. If the argument is negative, a
5266 * domain error occurs.
5267 * @returns Common logarithm of x.
5268 */
5269 template<DimensionlessUnitType UnitType>
5270 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> log10(const UnitType x) noexcept
5271 {
5272 return std::log10(x.value());
5273 }
5274
5278 * @details The integer part is stored in the object pointed by intpart, and the
5279 * fractional part is returned by the function. Both parts have the same sign
5280 * as x.
5281 * @param[in] x dimensionless value to break into parts.
5282 * @param[in] intpart Pointer to an object (of the same type as x) where the integral part
5283 * is stored with the same sign as x.
5284 * @returns The fractional part of x, with the same sign.
5285 */
5286 template<DimensionlessUnitType UnitType>
5287 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> modf(const UnitType x, UnitType* intpart) noexcept
5288 {
5289 using promoted = detail::floating_point_promotion_t<typename UnitType::underlying_type>;
5290 // std::modf splits the NORMALIZED value; the integral and fractional parts are already in the
5291 // quantity's own (normalized) units. Re-wrapping the fractional double through UnitType's
5292 // value constructor would re-apply the unit's scale (e.g. percent's 1/100) a second time, so the
5293 // fraction is returned as a plain dimensionless value and the integral part is converted back to
5294 // UnitType through its converting constructor.
5295 promoted intp;
5296 promoted fracpart = std::modf(x.template to<promoted>(), &intp);
5297 *intpart = dimensionless<promoted>{intp};
5298 return dimensionless<promoted>{fracpart};
5299 }
5304
5308 template<DimensionlessUnitType UnitType>
5309 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> exp2(const UnitType x) noexcept
5310 {
5311 return std::exp2(x.value());
5312 }
5313
5314 /**
5315 * @ingroup UnitMath
5316 * @brief Compute exponential minus one
5317 * @details Returns e raised to the power x minus one: e^x-1. For small magnitude values
5318 * of x, expm1 may be more accurate than exp(x)-1.
5319 * @param[in] x Value of the exponent.
5320 * @returns e raised to the power of x, minus one.
5321 */
5322 template<DimensionlessUnitType UnitType>
5323 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> expm1(const UnitType x) noexcept
5324 {
5325 return std::expm1(x.value());
5326 }
5327
5329 * @ingroup UnitMath
5330 * @brief Compute logarithm plus one
5331 * @details Returns the natural logarithm of one plus x. For small magnitude values of
5332 * x, logp1 may be more accurate than log(1+x).
5333 * @param[in] x Value whose logarithm is calculated. If the argument is less than -1, a
5334 * domain error occurs.
5335 * @returns The natural logarithm of (1+x).
5336 */
5337 template<DimensionlessUnitType UnitType>
5338 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> log1p(const UnitType x) noexcept
5339 {
5340 return std::log1p(x.value());
5341 }
5342
5343 /**
5344 * @ingroup UnitMath
5345 * @brief Compute binary logarithm
5346 * @details Returns the binary (base-2) logarithm of x.
5347 * @param[in] x Value whose logarithm is calculated. If the argument is negative, a
5348 * domain error occurs.
5349 * @returns The binary logarithm of x: log2x.
5350 */
5351 template<DimensionlessUnitType UnitType>
5352 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> log2(const UnitType x) noexcept
5353 {
5354 return std::log2(x.value());
5355 }
5356
5357 //----------------------------------
5358 // POWER FUNCTIONS
5359 //----------------------------------
5360
5361 /* pow is implemented earlier in the library since a lot of the unit definitions depend on it */
5362
5368 * @returns new unit, whose units are the square root of value's. E.g. if values
5369 * had units of `square_meter`, then the return type will have units of
5370 * `meter`.
5371 * @note `sqrt` provides a _rational approximation_ of the square root of <i>value</i>.
5372 * In some cases, _both_ the returned value _and_ conversion factor of the returned
5373 * unit type may have errors no larger than `1e-10`.
5374 */
5375 template<UnitType UnitType>
5376 requires(traits::has_linear_scale_v<UnitType>)
5377 constexpr auto sqrt(const UnitType& value) noexcept
5378 -> detail::rewrap_to_named_t<unit<traits::strong_t<square_root<typename traits::unit_traits<UnitType>::conversion_factor>>, detail::floating_point_promotion_t<typename traits::unit_traits<UnitType>::underlying_type>>>
5379 {
5380 return decltype(units::sqrt(value))(sqrt(value.raw()));
5381 }
5382
5385 * @brief Computes the square root of the sum-of-squares of x and y.
5386 * @details Only implemented for linear_scale units.
5387 * @param[in] x unit type value
5388 * @param[in] y unit type value
5389 * @returns square root of the sum-of-squares of x and y in the same units
5390 * as x.
5391 */
5392 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5393 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
5394 constexpr detail::floating_point_promotion_t<std::common_type_t<UnitTypeLhs, UnitTypeRhs>> hypot(const UnitTypeLhs& x, const UnitTypeRhs& y)
5395 {
5396 using CommonUnit = decltype(units::hypot(x, y));
5397 return CommonUnit(std::hypot(CommonUnit(x).raw(), CommonUnit(y).raw()));
5398 }
5399
5400 //----------------------------------
5401 // ROUNDING FUNCTIONS
5402 //----------------------------------
5407
5411 template<UnitType Unit>
5412 constexpr detail::floating_point_promotion_t<Unit> ceil(const Unit x) noexcept
5413 {
5414 return detail::floating_point_promotion_t<Unit>(std::ceil(x.raw()));
5415 }
5420
5424 template<UnitType Unit>
5425 constexpr detail::floating_point_promotion_t<Unit> floor(const Unit x) noexcept
5426 {
5427 return detail::floating_point_promotion_t<Unit>(std::floor(x.raw()));
5428 }
5429
5431 * @ingroup UnitMath
5432 * @brief Compute remainder of division
5433 * @details Returns the floating-point remainder of numer/denom (rounded towards zero).
5434 * @param[in] numer Value of the quotient numerator.
5435 * @param[in] denom Value of the quotient denominator.
5436 * @returns The remainder of dividing the arguments.
5437 */
5438 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5440 constexpr detail::floating_point_promotion_t<std::common_type_t<UnitTypeLhs, UnitTypeRhs>> fmod(const UnitTypeLhs numer, const UnitTypeRhs denom) noexcept
5441 {
5442 using CommonUnit = decltype(units::fmod(numer, denom));
5443 return CommonUnit(std::fmod(CommonUnit(numer).raw(), CommonUnit(denom).raw()));
5444 }
5445
5446 /**
5447 * @ingroup UnitMath
5448 * @brief Truncate value
5449 * @details Rounds x toward zero, returning the nearest integral value that is not
5450 * larger in magnitude than x. Effectively rounds towards 0.
5451 * @param[in] x Value to truncate
5452 * @returns The nearest integral value that is not larger in magnitude than x.
5453 */
5454 template<UnitType UnitType>
5455 constexpr detail::floating_point_promotion_t<UnitType> trunc(const UnitType x) noexcept
5456 {
5457 return detail::floating_point_promotion_t<UnitType>(std::trunc(x.raw()));
5458 }
5459
5460 /**
5461 * @ingroup UnitMath
5462 * @brief Round to nearest
5463 * @details Returns the integral value that is nearest to x, with halfway cases rounded
5464 * away from zero.
5465 * @param[in] x value to round.
5466 * @returns The value of x rounded to the nearest integral.
5467 */
5468 template<UnitType UnitType>
5469 constexpr detail::floating_point_promotion_t<UnitType> round(const UnitType x) noexcept
5470 {
5471 return detail::floating_point_promotion_t<UnitType>(std::round(x.raw()));
5472 }
5473 // DOXYGEN IGNORE
5475 namespace detail
5476 {
5478 enum class rounding_mode
5479 {
5480 toward_neg_infinity,
5481 toward_pos_infinity,
5482 nearest_half_away,
5483 toward_zero
5484 };
5485
5492 template<class Int>
5493 constexpr Int apply_integer_rounding(Int q, Int r, Int den, rounding_mode mode) noexcept
5494 {
5495 if (r == 0)
5496 return q; // exact — every mode agrees
5497 switch (mode)
5498 {
5499 case rounding_mode::toward_zero:
5500 return q; // integer division already truncated toward zero
5501 case rounding_mode::toward_neg_infinity:
5502 return r < 0 ? q - 1 : q; // a nonzero negative remainder means the true value is below q
5503 case rounding_mode::toward_pos_infinity:
5504 return r > 0 ? q + 1 : q; // a nonzero positive remainder means the true value is above q
5505 case rounding_mode::nearest_half_away:
5506 {
5507 // Halfway-away-from-zero: step away from zero when twice the remainder magnitude reaches den.
5508 const Int twiceRemainder = (r < 0 ? -r : r) * 2;
5509 if (twiceRemainder >= den)
5510 return r < 0 ? q - 1 : q + 1;
5511 return q;
5512 }
5513 }
5514 return q;
5515 }
5516
5525 template<class To, class From>
5526 constexpr To rounded_unit_cast(const From& x, rounding_mode mode) noexcept
5527 {
5528 using ToRep = typename To::underlying_type;
5529 using FromRep = typename From::underlying_type;
5530
5531 if constexpr (std::is_integral_v<FromRep>)
5532 {
5533 // Exact integer path: value (in From units) * num / den, rounded on the integer remainder.
5534 using Ratio = std::ratio_divide<typename From::conversion_factor::conversion_ratio, typename To::conversion_factor::conversion_ratio>;
5535 const widest_signed_int value = static_cast<widest_signed_int>(x.raw());
5536 const widest_signed_int product = value * static_cast<widest_signed_int>(Ratio::num);
5537 const widest_signed_int den = static_cast<widest_signed_int>(Ratio::den);
5538 const widest_signed_int quotient = product / den;
5539 const widest_signed_int remainder = product % den;
5540 const widest_signed_int rounded = apply_integer_rounding(quotient, remainder, den, mode);
5541 return To(static_cast<ToRep>(rounded), linearized_value);
5542 }
5543 else
5544 {
5545 // A floating-point source: express in the target unit and apply the matching std:: rounding.
5546 using Promoted = unit<typename To::conversion_factor, floating_point_promotion_t<ToRep>, typename To::numerical_scale_type>;
5547 const auto inTarget = Promoted(x).to_linearized();
5548 const auto rounded = mode == rounding_mode::toward_neg_infinity ? std::floor(inTarget)
5549 : mode == rounding_mode::toward_pos_infinity ? std::ceil(inTarget)
5550 : mode == rounding_mode::nearest_half_away ? std::round(inTarget)
5551 : std::trunc(inTarget);
5552 return To(static_cast<ToRep>(rounded), linearized_value);
5553 }
5554 }
5555
5560 template<class To, class From>
5561 inline constexpr bool is_roundable_unit_conversion =
5562 traits::is_unit_v<To> && traits::is_unit_v<From> && same_dimension<From, To> &&
5563 std::is_integral_v<typename To::underlying_type> && !is_losslessly_convertible_unit<From, To>;
5564 } // namespace detail // END DOXYGEN IGNORE
5566
5572 * number of bytes), `units::floor<bytes<int>>(someRuntimeBits)` states the rounding intent and
5573 * yields the number of whole bytes at or below the value. Same shape as `std::chrono::floor<To>`.
5574 * @tparam To the coarser integral target unit (e.g. `bytes<int>`).
5575 * @tparam From the source unit (deduced), same dimension as `To`.
5576 * @param[in] x the value to convert.
5577 * @return `x` in units of `To`, rounded toward negative infinity.
5578 */
5579 template<class To, UnitType From>
5580 requires detail::is_roundable_unit_conversion<To, From>
5581 constexpr To floor(const From& x) noexcept
5582 {
5583 return detail::rounded_unit_cast<To>(x, detail::rounding_mode::toward_neg_infinity);
5584 }
5585
5588 * @brief Convert to a coarser integral unit, rounding up (toward positive infinity).
5589 * @details Run-time lossy conversion with explicit rounding intent; see `floor<To>`.
5590 * @tparam To the coarser integral target unit.
5591 * @tparam From the source unit (deduced), same dimension as `To`.
5592 * @param[in] x the value to convert.
5593 * @return `x` in units of `To`, rounded toward positive infinity.
5594 */
5595 template<class To, UnitType From>
5596 requires detail::is_roundable_unit_conversion<To, From>
5597 constexpr To ceil(const From& x) noexcept
5598 {
5599 return detail::rounded_unit_cast<To>(x, detail::rounding_mode::toward_pos_infinity);
5600 }
5601
5604 * @brief Convert to a coarser integral unit, rounding to nearest (halfway away from zero).
5605 * @details Run-time lossy conversion with explicit rounding intent; see `floor<To>`.
5606 * @tparam To the coarser integral target unit.
5607 * @tparam From the source unit (deduced), same dimension as `To`.
5608 * @param[in] x the value to convert.
5609 * @return `x` in units of `To`, rounded to the nearest whole target unit.
5610 */
5611 template<class To, UnitType From>
5612 requires detail::is_roundable_unit_conversion<To, From>
5613 constexpr To round(const From& x) noexcept
5614 {
5615 return detail::rounded_unit_cast<To>(x, detail::rounding_mode::nearest_half_away);
5616 }
5617
5620 * @brief Convert to a coarser integral unit, rounding toward zero.
5621 * @details Run-time lossy conversion with explicit rounding intent; see `floor<To>`.
5622 * @tparam To the coarser integral target unit.
5623 * @tparam From the source unit (deduced), same dimension as `To`.
5624 * @param[in] x the value to convert.
5625 * @return `x` in units of `To`, rounded toward zero.
5626 */
5627 template<class To, UnitType From>
5628 requires detail::is_roundable_unit_conversion<To, From>
5629 constexpr To trunc(const From& x) noexcept
5630 {
5631 return detail::rounded_unit_cast<To>(x, detail::rounding_mode::toward_zero);
5632 }
5633
5634 //----------------------------------
5635 // FLOATING POINT MANIPULATION
5636 //----------------------------------
5637
5639 * @ingroup UnitMath
5640 * @brief Copy sign
5641 * @details Returns a value with the magnitude and dimension of x, and the sign of y.
5642 * Values x and y do not have to be compatible units.
5643 * @param[in] x Value with the magnitude of the resulting value.
5644 * @param[in] y Value with the sign of the resulting value.
5645 * @returns value with the magnitude and dimension of x, and the sign of y.
5647 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5648 constexpr detail::floating_point_promotion_t<UnitTypeLhs> copysign(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
5649 {
5650 return detail::floating_point_promotion_t<UnitTypeLhs>(std::copysign(x.raw(), y.raw())); // no need for conversion to get the correct sign.
5651 }
5652
5654 template<UnitType UnitTypeLhs, ArithmeticType T>
5655 constexpr detail::floating_point_promotion_t<UnitTypeLhs> copysign(const UnitTypeLhs x, const T& y) noexcept
5656 {
5657 return detail::floating_point_promotion_t<UnitTypeLhs>(std::copysign(x.raw(), y));
5658 }
5659
5660 //----------------------------------
5661 // MIN / MAX / DIFFERENCE
5662 //----------------------------------
5663
5665 * @ingroup UnitMath
5666 * @brief Positive difference
5667 * @details The function returns x-y if x>y, and zero otherwise, in their common type.
5668 * @param[in] x Values whose difference is calculated.
5669 * @param[in] y Values whose difference is calculated.
5670 * @returns The positive difference between x and y.
5671 */
5672 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5674 constexpr detail::floating_point_promotion_t<std::common_type_t<UnitTypeLhs, UnitTypeRhs>> fdim(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
5675 {
5676 using CommonUnit = decltype(units::fdim(x, y));
5677 return CommonUnit(std::fdim(CommonUnit(x).raw(), CommonUnit(y).raw()));
5678 }
5679
5681 * @ingroup UnitMath
5682 * @brief Maximum value
5683 * @details Returns the larger of its arguments: either x or y, in their common type.
5684 * @param[in] x Values among which the function selects a maximum.
5685 * @param[in] y Values among which the function selects a maximum.
5686 * @returns The maximum numeric value of its arguments.
5687 */
5688 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5690 constexpr detail::floating_point_promotion_t<std::common_type_t<UnitTypeLhs, UnitTypeRhs>> fmax(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
5691 {
5692 using CommonUnit = decltype(units::fmax(x, y));
5693 return CommonUnit(std::fmax(CommonUnit(x).raw(), CommonUnit(y).raw()));
5694 }
5695
5698 * @brief Minimum value
5699 * @details Returns the smaller of its arguments: either x or y, in their common type.
5700 * If one of the arguments in a NaN, the other is returned.
5701 * @param[in] x Values among which the function selects a minimum.
5702 * @param[in] y Values among which the function selects a minimum.
5703 * @returns The minimum numeric value of its arguments.
5704 */
5705 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5707 constexpr detail::floating_point_promotion_t<std::common_type_t<UnitTypeLhs, UnitTypeRhs>> fmin(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
5708 {
5709 using CommonUnit = decltype(units::fmin(x, y));
5710 return CommonUnit(std::fmin(CommonUnit(x).raw(), CommonUnit(y).raw()));
5711 }
5712
5713 //----------------------------------
5714 // OTHER FUNCTIONS
5715 //----------------------------------
5720
5724 template<UnitType UnitType>
5725 constexpr detail::floating_point_promotion_t<UnitType> fabs(const UnitType x) noexcept
5726 {
5727 return detail::floating_point_promotion_t<UnitType>(std::fabs(x.raw()));
5728 }
5733
5737 template<UnitType UnitType>
5738 constexpr UnitType abs(const UnitType x) noexcept
5739 {
5740 return UnitType(std::abs(x.raw()));
5741 }
5742
5752 * @param[in] x Value to be multiplied.
5753 * @param[in] y Value to be multiplied.
5754 * @param[in] z Value to be added.
5755 * @returns The result of x*y+z.
5756 */
5757 template<UnitType UnitTypeLhs, UnitType UnitMultiply, UnitType UnitAdd>
5758 requires(traits::is_same_dimension_conversion_factor_v<
5759 compound_conversion_factor<typename traits::unit_traits<UnitTypeLhs>::conversion_factor, typename traits::unit_traits<UnitMultiply>::conversion_factor>,
5760 typename traits::unit_traits<UnitAdd>::conversion_factor>)
5761 constexpr auto fma(const UnitTypeLhs x, const UnitMultiply y, const UnitAdd z) noexcept
5762 -> std::common_type_t<decltype(detail::floating_point_promotion_t<UnitTypeLhs>(x) * detail::floating_point_promotion_t<UnitMultiply>(y)), UnitAdd>
5763 {
5764 using CommonUnit = decltype(units::fma(x, y, z));
5765 using ProductUnit = decltype(detail::floating_point_promotion_t<UnitTypeLhs>(x) * detail::floating_point_promotion_t<UnitMultiply>(y));
5766
5767 // Fold the product-unit -> result-unit conversion into one multiplicand (a compile-time-constant
5768 // scale), so a SINGLE std::fma performs the multiply and the add in the result's basis with one
5769 // rounding: x_raw * (y_raw * scale) + z_in_result. Feeding the raw operands directly (each in its
5770 // own unit) would combine inconsistent bases and give a wrong result.
5771 constexpr auto scale = CommonUnit(ProductUnit(1)).raw();
5772 return CommonUnit(std::fma(x.raw(), y.raw() * scale, CommonUnit(z).raw()));
5773 }
5774
5775 //----------------------------
5776 // NAN support
5777 //----------------------------
5778
5779 template<UnitType UnitType>
5780 constexpr bool isnan(const UnitType& x) noexcept
5781 {
5782 return std::isnan(x.raw());
5783 }
5784
5785 template<UnitType UnitType>
5786 constexpr bool isinf(const UnitType& x) noexcept
5787 {
5788 return std::isinf(x.raw());
5789 }
5790
5791 template<UnitType UnitType>
5792 constexpr bool isfinite(const UnitType& x) noexcept
5793 {
5794 return std::isfinite(x.raw());
5795 }
5796
5797 template<UnitType UnitType>
5798 constexpr bool isnormal(const UnitType& x) noexcept
5799 {
5800 return std::isnormal(x.raw());
5801 }
5802
5803 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5805 constexpr bool isunordered(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
5806 {
5807 return std::isunordered(lhs.raw(), rhs.raw());
5808 }
5809} // end namespace units
5810
5811//----------------------------------------------------------------------------------------------------------------------
5812// STD Namespace extensions
5813//----------------------------------------------------------------------------------------------------------------------
5814
5815//------------------------------
5816// std::hash
5817//------------------------------
5818
5819template<class ConversionFactor, typename T, class NumericalScale>
5820struct std::hash<units::unit<ConversionFactor, T, NumericalScale>>
5821{
5822 template<typename U = T>
5823 constexpr std::size_t operator()(const units::unit<ConversionFactor, T, NumericalScale>& x) const noexcept
5824 {
5825 if constexpr (std::is_integral_v<U>)
5826 {
5827 return static_cast<std::size_t>(x.to_linearized());
5828 }
5829 else
5830 {
5831 return static_cast<std::size_t>(hash<T>()(x.to_linearized()));
5833 }
5834};
5835
5836// A NAMED unit is a class deriving from unit<...>; the exact-pattern specialization above does not match it, so its
5837// std::hash falls to the deleted primary. Inherit the base unit's hash (it operates on the linearized value, which the
5838// named unit has via its base) so a named unit is hashable exactly like the plain unit<...> it represents.
5839template<class Named>
5840 requires units::detail::is_named_unit_v<Named>
5841struct std::hash<Named> : std::hash<units::detail::unit_base_t<Named>>
5842{
5844
5845//----------------------------------------------------------------------------------------------------------------------
5846// NUMERIC LIMITS
5847//----------------------------------------------------------------------------------------------------------------------
5848
5849namespace std
5850{
5851 template<units::ConversionFactorType ConversionFactor, units::ArithmeticType T, units::NumericalScaleType<T> NonLinearScale>
5852 struct numeric_limits<units::unit<ConversionFactor, T, NonLinearScale>>
5853 {
5855 {
5856 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::min());
5857 }
5858
5859 static constexpr units::unit<ConversionFactor, T, NonLinearScale> denorm_min() noexcept
5860 {
5861 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::denorm_min());
5862 }
5863
5865 {
5866 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::max());
5867 }
5868
5869 static constexpr units::unit<ConversionFactor, T, NonLinearScale> lowest()
5870 {
5871 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::lowest());
5872 }
5873
5874 static constexpr units::unit<ConversionFactor, T, NonLinearScale> epsilon()
5875 {
5876 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::epsilon());
5877 }
5878
5879 static constexpr units::unit<ConversionFactor, T, NonLinearScale> round_error()
5880 {
5881 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::round_error());
5882 }
5883
5884 static constexpr units::unit<ConversionFactor, T, NonLinearScale> infinity()
5885 {
5886 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::infinity());
5887 }
5888
5889 static constexpr units::unit<ConversionFactor, T, NonLinearScale> quiet_NaN()
5890 {
5891 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::quiet_NaN());
5892 }
5893
5894 static constexpr units::unit<ConversionFactor, T, NonLinearScale> signaling_NaN()
5895 {
5896 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::signaling_NaN());
5897 }
5898
5899 static constexpr bool is_specialized = std::numeric_limits<T>::is_specialized;
5900 static constexpr bool is_signed = std::numeric_limits<T>::is_signed;
5901 static constexpr bool is_integer = std::numeric_limits<T>::is_integer;
5902 static constexpr bool is_exact = std::numeric_limits<T>::is_exact;
5903 static constexpr bool has_infinity = std::numeric_limits<T>::has_infinity;
5904 static constexpr bool has_quiet_NaN = std::numeric_limits<T>::has_quiet_NaN;
5905 static constexpr bool has_signaling_NaN = std::numeric_limits<T>::has_signaling_NaN;
5906 };
5907
5908 // A NAMED unit is a class deriving from unit<...>; the exact-pattern specialization above does not match it.
5909 // Return the NAMED type from each limit (the named unit converts from its base), so both the VALUE and the
5910 // reported TYPE match the named unit — generic code that asks for numeric_limits<meters<double>>::max() gets a
5911 // meters<double> back, not the plain unit<...> base.
5912 template<class Named>
5913 requires units::detail::is_named_unit_v<Named>
5914 struct numeric_limits<Named> : numeric_limits<units::detail::unit_base_t<Named>>
5915 {
5916 private:
5917 using Base = numeric_limits<units::detail::unit_base_t<Named>>;
5918
5919 public:
5920 // Inherit every flag/member from the base (has_infinity, is_signed, digits, ...); only SHADOW the
5921 // value-returning statics to return the NAMED type (the named unit converts from its base), so both the value
5922 // and the reported type match the named unit.
5923 static constexpr Named min() { return Named(Base::min()); }
5924 static constexpr Named max() { return Named(Base::max()); }
5925 static constexpr Named lowest() { return Named(Base::lowest()); }
5926 static constexpr Named epsilon() { return Named(Base::epsilon()); }
5927 static constexpr Named round_error() { return Named(Base::round_error()); }
5928 static constexpr Named denorm_min() { return Named(Base::denorm_min()); }
5929 static constexpr Named infinity() { return Named(Base::infinity()); }
5930 static constexpr Named quiet_NaN() { return Named(Base::quiet_NaN()); }
5931 static constexpr Named signaling_NaN() { return Named(Base::signaling_NaN()); }
5932 };
5933
5934 // These overloads accept ANY unit — including a NAMED unit, which is a class DERIVING from units::unit<...>.
5935 // Constraining on the units::UnitType concept (rather than an exact `unit<Cf,T,Ns>&` parameter) makes a named
5936 // unit an EXACT match, so it wins over <cmath>'s own generic isnan/isinf/... templates. With the exact-type
5937 // parameter, a named (derived) unit only bound via a derived->base conversion — a WORSE match than <cmath>'s
5938 // template — so on some standard libraries (MSVC) the generic <cmath> overload was selected and forwarded the
5939 // unit to fpclassify(), which has no unit overload (error C2665). raw() yields the arithmetic magnitude.
5940 template<units::UnitType U>
5941 constexpr bool isnan(U x)
5942 {
5943 return std::isnan(x.raw());
5944 }
5945
5946 template<units::UnitType U>
5947 constexpr bool isinf(U x)
5948 {
5949 return std::isinf(x.raw());
5950 }
5951
5952 template<units::UnitType U>
5953 constexpr bool isfinite(U x)
5954 {
5955 return std::isfinite(x.raw());
5956 }
5957
5958 template<units::UnitType U>
5959 constexpr bool signbit(U x)
5960 {
5961 return std::signbit(x.raw());
5962 }
5963} // namespace std
5964
5965//------------------------------
5966// UNIT DEDUCTION GUIDES
5967//------------------------------
5968
5969namespace units
5970{
5971 // Concept to ensure we only apply the dimensionless fallback
5972 // to a pure, unmodified dimensionless unit.
5973 template<class Cf>
5974 concept PureDimensionlessCF = std::is_same_v<typename Cf::dimension_type, dimension::dimensionless> && std::ratio_equal_v<typename Cf::conversion_ratio, std::ratio<1>> &&
5975 std::ratio_equal_v<typename Cf::pi_exponent_ratio, std::ratio<0>> && std::ratio_equal_v<typename Cf::translation_ratio, std::ratio<0>>;
5976
5977 // 1) chrono deduction guide
5978 template<ArithmeticType Rep, RatioType Period>
5979 unit(std::chrono::duration<Rep, Period>) -> unit<conversion_factor<Period, dimension::time>, Rep>;
5980
5981 // 2) Dimensionless fallback:
5982 // Only applies if the source is exactly the base dimensionless unit.
5983 template<ArithmeticType SourceTy, ConversionFactorType SourceCf>
5984 requires(traits::is_unit_v<unit<SourceCf, SourceTy>> && PureDimensionlessCF<SourceCf>)
5985 unit(const unit<SourceCf, SourceTy>&) -> unit<conversion_factor<std::ratio<1>, dimension::dimensionless>, SourceTy>;
5986
5987 // 3) Lossless integral conversion:
5988 // For dimensionally compatible units where the conversion is integral and lossless.
5989 // This applies only if is_losslessly_convertible_unit is true.
5990 template<ArithmeticType SourceTy, ConversionFactorType SourceCf, ConversionFactorType TargetCf = SourceCf>
5991 requires(traits::is_unit_v<unit<SourceCf, SourceTy>> && traits::is_conversion_factor_v<TargetCf> && traits::is_same_dimension_conversion_factor_v<SourceCf, TargetCf> &&
5992 !std::is_same_v<SourceCf, TargetCf> && detail::is_losslessly_convertible_unit<unit<SourceCf, SourceTy>, unit<TargetCf, SourceTy>>)
5994
5995 // 4) Non-lossless conversions:
5996 // For dimensionally compatible units where integral conversion is not possible.
5997 // Falls back to floating point.
5998 template<ArithmeticType SourceTy, ConversionFactorType SourceCf, ConversionFactorType TargetCf = SourceCf>
5999 requires(traits::is_unit_v<unit<SourceCf, SourceTy>> && traits::is_conversion_factor_v<TargetCf> && traits::is_same_dimension_conversion_factor_v<SourceCf, TargetCf> &&
6000 !std::is_same_v<SourceCf, TargetCf> && !detail::is_losslessly_convertible_unit<unit<SourceCf, SourceTy>, unit<TargetCf, SourceTy>>)
6002
6003 // 5) Exact matches:
6004 // If the unit already matches `unit<TargetCf, SourceTy>`, use it directly.
6005 template<ConversionFactorType TargetCf, ArithmeticType SourceTy>
6006 requires traits::is_unit_v<unit<TargetCf, SourceTy>>
6008
6009 // 6) Deduce type from arithmetic type (dimensionless by default)
6010 template<typename T, typename Cf = dimension::dimensionless, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
6011 unit(T) -> unit<Cf, T>;
6012} // namespace units
6013
6014//----------------------------------------------------------------------------------------------------------------------
6015// std::format SUPPORT
6016//----------------------------------------------------------------------------------------------------------------------
6017
6018#if defined(UNIT_LIB_ENABLE_FORMAT)
6019
6020//----------------------------------------------------------------------------------------------------------------------
6021// CLASS: std::formatter<units::unit<...>, char>
6022//----------------------------------------------------------------------------------------------------------------------
6039//----------------------------------------------------------------------------------------------------------------------
6040template<units::UnitType U>
6041struct std::formatter<U, char>
6042{
6043 using conversion_factor = typename units::traits::unit_traits<U>::conversion_factor;
6044 using value_type = typename units::traits::unit_traits<U>::underlying_type;
6045 using scale_type = typename units::traits::unit_traits<U>::numerical_scale_type;
6046 using promoted_value_type = units::detail::floating_point_promotion_t<value_type>;
6047
6048 // A named unit prints its stored value as-is, so its value formatter is the underlying type — integer
6049 // specs (d/x/b/…) then work for an integer-underlying unit. An unnamed unit is rendered in its base
6050 // unit, a conversion that is floating-point, so its value formatter is the promoted type.
6051 static constexpr bool renders_in_base_unit = units::detail::label_uses_base_unit<conversion_factor, value_type, scale_type>();
6052 using formatted_value_type = std::conditional_t<renders_in_base_unit, promoted_value_type, value_type>;
6053
6054 // The %b flag base-converts a NAMED unit's value to SI, which is a floating-point result; it is emitted
6055 // through a promoted-type formatter. (For an unnamed unit the primary formatter is already promoted.)
6056 std::formatter<formatted_value_type, char> m_valueFormatter;
6057 std::formatter<promoted_value_type, char> m_baseFormatter;
6058 units::detail::unit_format_options m_options;
6059 bool m_usesBaseFormatter = false;
6060
6061 //----------------------------------------------------------------------------------------------------------------------
6062 // FUNCTION: parse [public]
6063 //----------------------------------------------------------------------------------------------------------------------
6068 //----------------------------------------------------------------------------------------------------------------------
6069 constexpr auto parse(std::format_parse_context& ctx)
6070 {
6071 auto it = ctx.begin();
6072 auto end = ctx.end();
6073
6074 // The value-spec runs to the first '%' (or to the closing '}').
6075 auto valueSpecEnd = it;
6076 for (auto scan = it; scan != end && *scan != '}'; ++scan)
6077 {
6078 if (*scan == '%')
6079 break;
6080 valueSpecEnd = scan + 1;
6081 }
6082
6083 // The %b flag (base-SI conversion) needs the promoted-type formatter; every other flag uses the
6084 // stored-type formatter. Determine which is in play by scanning the unit-opts for 'b' before
6085 // delegating the value-spec, so the value-spec is parsed into exactly the formatter that will emit
6086 // it (parsing an integer spec such as `d` into a floating-point formatter would wrongly reject it).
6087 m_usesBaseFormatter = false;
6088 for (auto scan = valueSpecEnd; scan != end && *scan != '}'; ++scan)
6089 {
6090 if (*scan == 'b')
6091 {
6092 m_usesBaseFormatter = true;
6093 break;
6094 }
6095 }
6096
6097 // Delegate the value-spec to the chosen value formatter. Present it a parse context spanning only
6098 // the value-spec and require it consumed the whole thing.
6099 if (valueSpecEnd != it)
6100 {
6101 std::string_view valueSpec(it, valueSpecEnd);
6102 if (m_usesBaseFormatter)
6103 {
6104 std::format_parse_context baseCtx(valueSpec);
6105 if (m_baseFormatter.parse(baseCtx) != valueSpec.end())
6106 throw std::format_error("units: invalid value format-spec");
6107 }
6108 else
6109 {
6110 std::format_parse_context valueCtx(valueSpec);
6111 if (m_valueFormatter.parse(valueCtx) != valueSpec.end())
6112 throw std::format_error("units: invalid value format-spec");
6113 }
6114 }
6115
6116 it = valueSpecEnd;
6117
6118 // Unit-opts after '%'.
6119 if (it != end && *it == '%')
6120 {
6121 ++it;
6122 bool sawForm = false;
6123 bool sawShow = false;
6124 while (it != end && *it != '}')
6125 {
6126 const char c = *it;
6127 if (c == 'a' || c == 'n' || c == 'b')
6128 {
6129 if (sawForm)
6130 throw std::format_error("units: duplicate label-form flag");
6131 sawForm = true;
6132 m_options.form = (c == 'a') ? units::detail::label_form::abbreviation
6133 : (c == 'n') ? units::detail::label_form::name
6134 : units::detail::label_form::base;
6135 ++it;
6136 }
6137 else if (c == 'v' || c == 'u')
6138 {
6139 if (sawShow)
6140 throw std::format_error("units: duplicate show flag");
6141 sawShow = true;
6142 m_options.showValue = (c == 'v');
6143 m_options.showUnit = (c == 'u');
6144 ++it;
6145 }
6146 else if (c == '\'')
6147 {
6148 ++it; // opening quote
6149 std::string sep;
6150 bool closed = false;
6151 while (it != end && *it != '}')
6152 {
6153 if (*it == '\\')
6154 {
6155 ++it;
6156 if (it == end || *it == '}')
6157 throw std::format_error("units: dangling escape in separator");
6158 switch (*it)
6159 {
6160 case 't': sep.push_back('\t'); break;
6161 case 'n': sep.push_back('\n'); break;
6162 case '\\': sep.push_back('\\'); break;
6163 case '\'': sep.push_back('\''); break;
6164 default: sep.push_back(*it); break;
6165 }
6166 ++it;
6167 }
6168 else if (*it == '\'')
6169 {
6170 closed = true;
6171 ++it; // closing quote
6172 break;
6173 }
6174 else
6175 {
6176 sep.push_back(*it);
6177 ++it;
6178 }
6179 }
6180 if (!closed)
6181 throw std::format_error("units: unterminated separator literal");
6182 m_options.separator = std::move(sep);
6183 m_options.customSep = true;
6184 }
6185 else
6186 {
6187 throw std::format_error("units: unknown unit-format flag");
6188 }
6189 }
6190 }
6191
6192 return it;
6193 }
6194
6195 //----------------------------------------------------------------------------------------------------------------------
6196 // FUNCTION: format [public]
6197 //----------------------------------------------------------------------------------------------------------------------
6203 //----------------------------------------------------------------------------------------------------------------------
6204 template<class FormatContext>
6205 auto format(const U& obj, FormatContext& ctx) const
6206 {
6207 using base_unit_type = units::unit<units::conversion_factor<std::ratio<1>, typename conversion_factor::dimension_type>, promoted_value_type, scale_type>;
6208
6209 // The value: an unnamed unit is always rendered in its base unit (its honest label is the
6210 // base-dimension list); the %b flag likewise base-converts a named unit's value so the base-SI
6211 // label is honest. Otherwise a named unit shows its stored value as-is (so integer specs work).
6212 formatted_value_type value{};
6213 promoted_value_type baseValue{};
6214 if constexpr (renders_in_base_unit)
6215 value = base_unit_type(obj).raw();
6216 else
6217 value = static_cast<formatted_value_type>(obj.raw());
6218 if (m_options.form == units::detail::label_form::base)
6219 baseValue = base_unit_type(obj).raw();
6220
6221 std::string label;
6222 if (m_options.showUnit)
6223 {
6224 switch (m_options.form)
6225 {
6226 case units::detail::label_form::name: label = units::detail::unit_label<units::detail::label_form::name>(obj); break;
6227 case units::detail::label_form::base: label = units::detail::unit_label<units::detail::label_form::base>(obj); break;
6228 case units::detail::label_form::abbreviation:
6229 default: label = units::detail::unit_label<units::detail::label_form::abbreviation>(obj); break;
6230 }
6231 }
6232
6233 auto out = ctx.out();
6234
6235 if (m_options.showValue)
6236 {
6237 if (m_usesBaseFormatter)
6238 {
6239 // %b: emit the base-SI value through the promoted-type formatter. For an unnamed unit the
6240 // value is already the promoted base value; for a named unit it is the base-converted one.
6241 const promoted_value_type emitted = renders_in_base_unit ? static_cast<promoted_value_type>(value) : baseValue;
6242 out = m_baseFormatter.format(emitted, ctx);
6243 }
6244 else
6245 {
6246 out = m_valueFormatter.format(value, ctx);
6247 }
6248 }
6249
6250 if (m_options.showUnit && !label.empty())
6251 {
6252 // The core builders prefix a label with a single space (the default separator). Keep it when no
6253 // separator was overridden and a value precedes the label; otherwise strip it and, for a shown
6254 // value, emit the chosen separator.
6255 std::string_view labelView(label);
6256 const bool hasLeadingSpace = !labelView.empty() && labelView.front() == ' ';
6257
6258 if (m_options.showValue)
6259 {
6260 if (m_options.customSep)
6261 {
6262 if (hasLeadingSpace)
6263 labelView.remove_prefix(1);
6264 for (char ch : m_options.separator)
6265 *out++ = ch;
6266 }
6267 }
6268 else
6269 {
6270 if (hasLeadingSpace)
6271 labelView.remove_prefix(1);
6272 }
6273
6274 for (char ch : labelView)
6275 *out++ = ch;
6276 }
6277
6278 return out;
6279 }
6280};
6281
6282#endif // UNIT_LIB_ENABLE_FORMAT
6283
6284//----------------------------------------------------------------------------------------------------------------------
6285// JSON SUPPORT
6286//----------------------------------------------------------------------------------------------------------------------
6287
6288#if defined __has_include
6289#if __has_include(<nlohmann/json.hpp>)
6290#include <nlohmann/json.hpp>
6291namespace units
6292{
6293 template<class UnitType>
6294 requires(units::traits::is_unit_v<UnitType>)
6295 void from_json(const nlohmann::json& j, UnitType& u)
6296 {
6297 using underlying = typename units::traits::unit_traits<UnitType>::underlying_type;
6298 underlying value;
6299 j.get_to(value);
6300 u = UnitType(value);
6301 }
6302
6303 template<class UnitType>
6304 requires(units::traits::is_unit_v<UnitType>)
6305 void to_json(nlohmann::json& j, const UnitType& u)
6306 {
6307 j = u.raw();
6308 }
6309} // namespace units
6310#endif
6311#endif
6312
6313#endif // UNIT_CORE_H
Definition core.h:2735
ConversionFactor conversion_factor
Definition core.h:2740
constexpr auto value() const noexcept
Definition core.h:2989
constexpr T to_linearized() const noexcept
linearized unit value
Definition core.h:3046
T value_type
Definition core.h:2739
constexpr unit(const unit< ConversionFactorRhs, Ty, NsRhs > &rhs) noexcept
converting constructor
Definition core.h:2761
constexpr unit< Cf, Ty > convert() const noexcept
Definition core.h:3062
constexpr bool operator!=(const unit< ConversionFactorRhs, Ty, NsRhs > &rhs) const noexcept
Definition core.h:2966
constexpr underlying_type raw() const noexcept
Definition core.h:2977
T _linearized_value
Definition core.h:3179
constexpr unit & operator=(const underlying_type &rhs) noexcept
assignment
Definition core.h:2875
constexpr unit & operator=(const unit &rhs) noexcept=default
constexpr Ty to() const noexcept
Definition core.h:3021
T underlying_type
Definition core.h:2738
Concept for types which represent arithmetic types.
Definition core.h:987
Concept for types which represent conversion factors.
Definition core.h:1008
Concept for types which represent units with a dimension (i.e.
Definition core.h:1029
Concept satisfied by any unit whose SI dimension is dimensionless; being dimension-keyed it.
Definition core.h:3865
Concept for types which represent units without a dimension (dimensionless).
Definition core.h:1036
Concept for types which represent non-arithmetic types.
Definition core.h:994
Concept for types which represent numerical scales.
Definition core.h:1015
Definition core.h:5965
Definition core.h:1531
Concept for types which represent std::ratios.
Definition core.h:1001
Concept for types which represent units.
Definition core.h:1022
Concept for types which represent units of the same dimensionality.
Definition core.h:1050
unit, dimensional analysis, generic cmath functions, traits (not dimension-specific),...
#define UNIT_ADD_SCALED_UNIT_DEFINITION(unitName, scale,...)
Macro for generating the boilerplate code for the scaled unit template definition.
Definition core.h:267
label_form
Builds the unit-label suffix for a unit — the text that follows its numeric value.
Definition core.h:3416
@ base
the SI base-dimension list (" m s^-1"); pairs with a base-converted value.
Definition core.h:3419
@ name
the unit's own full name ("meters", "feet"); base-dimension list if unnamed.
Definition core.h:3418
@ abbreviation
the unit's own abbreviation ("m", "ft"), the default; base-dimension list if unnamed.
Definition core.h:3417
constexpr bool label_uses_base_unit()
Whether a unit's label is its dimension list rather than a named abbreviation.
Definition core.h:3469
std::string dimension_to_string(const dim< D, E > &)
Renders a single dimension term (base dimension + exponent) as text.
Definition core.h:3359
#define UNIT_ADD_DIMENSION_TRAIT(unitdimension, ConceptName)
Macro to create the is_dimension_unit type trait and the ConceptName concept.
Definition core.h:488
ConversionFactor strong_name(ConversionFactor *,...)
ADL customization point that maps a conversion_factor to its friendly strong type.
unit< typename T::conversion_factor, typename T::underlying_type, typename T::numerical_scale_type > unit_base_t
Maps any unit type to the canonical unit<Cf, Underlying, Scale> it represents.
Definition core.h:3194
typename detail::prefix< std::ratio< 1152921504606846976 >, Cf >::type exbi
< Represents the type of class Cf with the binary 'pebi' prefix appended.
Definition core.h:2020
typename detail::prefix< std::ratio< 1125899906842624 >, Cf >::type pebi
< Represents the type of class Cf with the binary 'tebi' prefix appended.
Definition core.h:2019
typename detail::prefix< std::ratio< 1073741824 >, Cf >::type gibi
< Represents the type of class Cf with the binary 'mibi' prefix appended.
Definition core.h:2017
typename detail::prefix< std::ratio< 1048576 >, Cf >::type mebi
< Represents the type of class Cf with the binary 'kibi' prefix appended.
Definition core.h:2016
typename detail::prefix< std::ratio< 1099511627776 >, Cf >::type tebi
< Represents the type of class Cf with the binary 'gibi' prefix appended.
Definition core.h:2018
typename detail::prefix< std::ratio< 1024 >, Cf >::type kibi
< Represents the type of class Cf with the metric 'exa' prefix appended.
Definition core.h:2015
typename detail::compound_impl< Cf, Cfs... >::type compound_conversion_factor
Represents a conversion factor made up from other conversion factors.
Definition core.h:1953
constexpr T unit_cast(const Unit &value) noexcept
Casts an unit to an arithmetic type.
Definition core.h:3754
constexpr To convert(const From &value) noexcept
converts a value from an unit to another.
Definition core.h:2345
typename detail::prefix< std::deci, Cf >::type deci
< Represents the type of class Cf with the metric 'centi' prefix appended.
Definition core.h:2000
typename detail::prefix< std::centi, Cf >::type centi
< Represents the type of class Cf with the metric 'milli' prefix appended.
Definition core.h:1999
typename detail::prefix< std::giga, Cf >::type giga
< Represents the type of class Cf with the metric 'mega' prefix appended.
Definition core.h:2005
typename detail::prefix< std::micro, Cf >::type micro
< Represents the type of class Cf with the metric 'nano' prefix appended.
Definition core.h:1997
typename detail::prefix< std::mega, Cf >::type mega
< Represents the type of class Cf with the metric 'kilo' prefix appended.
Definition core.h:2004
typename detail::prefix< std::exa, Cf >::type exa
< Represents the type of class Cf with the metric 'peta' prefix appended.
Definition core.h:2008
typename detail::prefix< std::femto, Cf >::type femto
< Represents the type of class Cf with the metric 'atto' prefix appended.
Definition core.h:1994
typename detail::prefix< std::pico, Cf >::type pico
< Represents the type of class Cf with the metric 'femto' prefix appended.
Definition core.h:1995
typename detail::prefix< std::kilo, Cf >::type kilo
< Represents the type of class Cf with the metric 'hecto' prefix appended.
Definition core.h:2003
typename detail::prefix< std::milli, Cf >::type milli
< Represents the type of class Cf with the metric 'micro' prefix appended.
Definition core.h:1998
typename detail::prefix< std::peta, Cf >::type peta
< Represents the type of class Cf with the metric 'tera' prefix appended.
Definition core.h:2007
typename detail::prefix< std::nano, Cf >::type nano
< Represents the type of class Cf with the metric 'pico' prefix appended.
Definition core.h:1996
typename detail::prefix< std::tera, Cf >::type tera
< Represents the type of class Cf with the metric 'giga' prefix appended.
Definition core.h:2006
typename detail::prefix< std::deca, Cf >::type deca
< Represents the type of class Cf with the metric 'deci' prefix appended.
Definition core.h:2001
typename detail::prefix< std::hecto, Cf >::type hecto
< Represents the type of class Cf with the metric 'deca' prefix appended.
Definition core.h:2002
std::is_invocable_r< Ret, detail::invocable_scale< T >, Ret > is_numerical_scale
Trait which tests whether T meets the requirements for a numerical scale.
Definition core.h:972
typename units::detail::Sqrt< Ratio, std::ratio< 1, Eps > >::type ratio_sqrt
Calculate square root of a ratio at compile-time.
Definition core.h:1869
typename detail::sqrt_impl< Cf, Eps >::type square_root
represents the square root of type class U.
Definition core.h:1911
typename detail::cubed_impl< Cf >::type cubed
represents the type of class U cubed.
Definition core.h:1695
typename detail::squared_impl< Cf >::type squared
represents the unit type of class U squared
Definition core.h:1668
typename detail::inverse_impl< Cf >::type inverse
represents the inverse unit type of class U.
Definition core.h:1641
constexpr detail::floating_point_promotion_t< UnitType > trunc(const UnitType x) noexcept
Truncate value.
Definition core.h:5446
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > modf(const UnitType x, UnitType *intpart) noexcept
Break into fractional and integral parts.
Definition core.h:5278
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > log1p(const UnitType x) noexcept
Compute logarithm plus one.
Definition core.h:5329
constexpr detail::floating_point_promotion_t< std::common_type_t< UnitTypeLhs, UnitTypeRhs > > fmax(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
Maximum value.
Definition core.h:5681
constexpr detail::floating_point_promotion_t< UnitType > fabs(const UnitType x) noexcept
Compute absolute value.
Definition core.h:5716
constexpr detail::floating_point_promotion_t< std::common_type_t< UnitTypeLhs, UnitTypeRhs > > fmin(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
Minimum value.
Definition core.h:5698
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > log2(const UnitType x) noexcept
Compute binary logarithm.
Definition core.h:5343
constexpr detail::floating_point_promotion_t< std::common_type_t< UnitTypeLhs, UnitTypeRhs > > hypot(const UnitTypeLhs &x, const UnitTypeRhs &y)
Computes the square root of the sum-of-squares of x and y.
Definition core.h:5385
constexpr auto fma(const UnitTypeLhs x, const UnitMultiply y, const UnitAdd z) noexcept -> std::common_type_t< decltype(detail::floating_point_promotion_t< UnitTypeLhs >(x) *detail::floating_point_promotion_t< UnitMultiply >(y)), UnitAdd >
Multiply-add.
Definition core.h:5752
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > exp(const UnitType x) noexcept
Compute exponential function.
Definition core.h:5232
constexpr detail::floating_point_promotion_t< UnitType > round(const UnitType x) noexcept
Round to nearest.
Definition core.h:5460
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > exp2(const UnitType x) noexcept
Compute binary exponential function.
Definition core.h:5300
constexpr detail::floating_point_promotion_t< Unit > ceil(const Unit x) noexcept
Round up value.
Definition core.h:5403
constexpr detail::floating_point_promotion_t< UnitTypeLhs > copysign(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
Copy sign.
Definition core.h:5639
constexpr detail::floating_point_promotion_t< std::common_type_t< UnitTypeLhs, UnitTypeRhs > > fdim(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
Positive difference.
Definition core.h:5665
constexpr detail::floating_point_promotion_t< Unit > floor(const Unit x) noexcept
Round down value.
Definition core.h:5416
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > log(const UnitType x) noexcept
Compute natural logarithm.
Definition core.h:5247
constexpr detail::floating_point_promotion_t< std::common_type_t< UnitTypeLhs, UnitTypeRhs > > fmod(const UnitTypeLhs numer, const UnitTypeRhs denom) noexcept
Compute remainder of division.
Definition core.h:5431
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > log10(const UnitType x) noexcept
Compute common logarithm.
Definition core.h:5261
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > expm1(const UnitType x) noexcept
Compute exponential minus one.
Definition core.h:5314
#define MSVC_EBO
Describes objects that represent quantities of a given unit.
Definition core.h:2731
constexpr UnitType make_unit(const T value) noexcept
Constructs a unit container from an arithmetic type.
Definition core.h:3335
STL namespace.
constexpr unit< compound_conversion_factor< joules_, inverse< kelvin_ >, inverse< mols_ > > > R(8.314462618)
Gas constant.
constexpr meters_per_second c(299792458.0)
Speed of light in vacuum.
namespace representing the implemented base and derived unit types.
Definition core.h:1267
make_dimension< length, std::ratio< 2 >, time, std::ratio<-2 > > radioactivity
< Represents an SI derived unit of luminance
Definition core.h:1359
dimension_multiply< pressure, time > dynamic_viscosity
< Represents an SI derived unit of density
Definition core.h:1375
dimension_divide< mass, volume > density
< Represents an SI derived unit of torque
Definition core.h:1374
make_dimension< luminous_intensity, std::ratio< 1 >, length, std::ratio<-2 > > luminance
< Represents an SI derived unit of illuminance
Definition core.h:1358
dimension_pow< angle, std::ratio< 2 > > solid_angle
< Represents a quantity of angle
Definition core.h:1337
dimension_divide< current, voltage > conductance
< Represents an SI derived unit of impedance
Definition core.h:1353
make_dimension< power, std::ratio< 1 >, length, std::ratio<-1 > > spectral_flux
< Represents an SI derived unit of spectral intensity
Definition core.h:1367
dimension_divide< mass, substance > substance_mass
< Represents an SI derived unit of radioactivity
Definition core.h:1360
make_dimension< radiant_intensity, std::ratio< 1 >, area, std::ratio<-1 > > radiance
< Represents an SI derived unit of radiant intensity
Definition core.h:1364
dimension_divide< voltage, current > impedance
< Represents an SI derived unit of capacitance
Definition core.h:1352
dimension_divide< substance, mass > substance_concentration
< Represents an SI derived unit of substance mass
Definition core.h:1361
dimension_divide< energy, time > power
< Represents an SI derived unit of energy
Definition core.h:1349
dimension_multiply< impedance, time > inductance
< Represents an SI derived unit of magnetic flux
Definition core.h:1355
dimension_pow< length, std::ratio< 3 > > volume
< Represents an SI derived unit of area
Definition core.h:1344
dimension_divide< area, time > kinematic_viscosity
< Represents an SI derived unit of dynamic (absolute) viscosity
Definition core.h:1376
make_dimension< angle_tag > angle
< Represents a quantity with no dimension.
Definition core.h:1334
dimension_divide< velocity, time > acceleration
< Represents an SI derived unit of angular velocity
Definition core.h:1341
dimension_multiply< mass, acceleration > force
< Represents an SI derived unit of acceleration
Definition core.h:1342
make_dimension< power, std::ratio< 1 >, solid_angle, std::ratio<-1 > > radiant_intensity
< Represents an SI derived unit of magnetic field strength
Definition core.h:1363
dimension_divide< force, area > pressure
< Represents an SI derived unit of volumetric flow rate
Definition core.h:1346
make_dimension< radiant_intensity, std::ratio< 1 >, length, std::ratio<-1 > > spectral_intensity
< Represents an SI derived unit of irradiance
Definition core.h:1366
dimension_divide< power, current > voltage
< Represents an SI derived unit of power
Definition core.h:1350
dimension_divide< charge, voltage > capacitance
< Represents an SI derived unit of voltage
Definition core.h:1351
dimension_multiply< time, current > charge
< Represents an SI derived unit of pressure
Definition core.h:1347
make_dimension< mass, std::ratio< 1 >, time, std::ratio<-2 >, current, std::ratio<-1 > > magnetic_field_strength
< Represents an SI derived unit of substance concentration
Definition core.h:1362
dimension_divide< energy, current > magnetic_flux
< Represents an SI derived unit of conductance
Definition core.h:1354
dimension_multiply< force, length > energy
< Represents an SI derived unit of charge
Definition core.h:1348
make_dimension< power, std::ratio< 1 >, volume, std::ratio<-1 > > spectral_irradiance
< Represents an SI derived unit of spectral intensity
Definition core.h:1369
make_dimension< volume, std::ratio<-1 > > concentration
< Represents an SI derived unit of energy density
Definition core.h:1378
make_dimension< data_tag > data
< Represents a unit of concentration
Definition core.h:1379
dimension_multiply< solid_angle, luminous_intensity > luminous_flux
< Represents an SI derived unit of inductance
Definition core.h:1356
dimension_divide< length, time > velocity
< Represents an SI derived unit of frequency
Definition core.h:1339
dimension_divide< data, time > data_transfer_rate
< Represents a unit of data size
Definition core.h:1380
dimension_pow< length, std::ratio< 2 > > area
< Represents an SI derived unit of force
Definition core.h:1343
make_dimension< radiant_intensity, std::ratio< 1 >, volume, std::ratio<-1 > > spectral_radiance
< Represents an SI derived unit of spectral flux
Definition core.h:1368
make_dimension< luminous_flux, std::ratio< 1 >, length, std::ratio<-2 > > illuminance
< Represents an SI derived unit of luminous flux
Definition core.h:1357
make_dimension< time, std::ratio<-1 > > frequency
< Represents an SI derived unit of solid angle
Definition core.h:1338
make_dimension< power, std::ratio< 1 >, area, std::ratio<-1 > > irradiance
< Represents an SI derived unit of radiance
Definition core.h:1365
dimension_divide< angle, time > angular_velocity
< Represents an SI derived unit of velocity
Definition core.h:1340
make_dimension< energy, std::ratio< 1 >, volume, std::ratio<-1 > > energy_density
< Represents an SI derived unit of kinematic viscosity
Definition core.h:1377
make_dimension< length, std::ratio< 1 >, time, std::ratio<-3 > > jerk
< Represents an SI derived unit of spectral irradiance
Definition core.h:1372
dimension_divide< volume, time > volume_flow_rate
< Represents an SI derived unit of volume
Definition core.h:1345
dimension_multiply< force, length > torque
< Represents an SI derived unit of jerk
Definition core.h:1373
namespace for unit literal definitions of all categories.
namespace representing type traits which can access the properties of types provided by the units lib...
Definition core.h:197
constexpr bool is_affine_conversion_factor_v
true when a conversion factor carries a non-zero datum offset — i.e.
Definition core.h:2058
typename std::is_base_of< units::detail::_conversion_factor, T >::type is_conversion_factor
Definition core.h:852
detail::is_ratio_impl< T > is_ratio
UnaryTypeTrait for querying whether T represents a specialization of std::ratio.
Definition core.h:761
typename units::detail::dimension_of_impl< U >::type dimension_of_t
Names the dimension_t of a conversion_factor.
Definition core.h:1455
constexpr bool is_affine_unit_v
true when a unit type is affine — its conversion factor carries a non-zero datum offset (e....
Definition core.h:2559
Unit Conversion Library namespace.
Definition units.h:106
decibels() -> decibels< double >
Nullary guide so bare default-construction decibels{} / decibels() deduces decibels<default> — again ...
Definition core.h:1141
Type representing an arbitrary conversion factor between units.
Definition core.h:1563
numerical scale which is decibel
Definition core.h:5030
static T linearize(const T value) noexcept
linearizes value
Definition core.h:5038
static T scale(const T value) noexcept
returns value in dB
Definition core.h:5056
dimensionless unit with decibel scale
Definition core.h:5070
helper type to identify units.
Definition core.h:871
Whether T is a complete type, decided by SFINAE on sizeof(T) without instantiating any other trait.
Definition core.h:886
Definition core.h:3202
is_unit implementation: an incomplete or non-class type is never a unit, decided WITHOUT instantiatin...
Definition core.h:901
Definition core.h:3240
Definition core.h:3278
Definition core.h:1097
Definition core.h:1312
Definition core.h:1288
Definition core.h:1270
Definition core.h:1276
Definition core.h:1300
Definition core.h:1294
Definition core.h:1282
Definition core.h:1216
Definition core.h:1107
Definition core.h:1103
numerical scale which is linear
Definition core.h:3816
static constexpr T scale(const T value) noexcept
scales value
Definition core.h:3836
static constexpr T linearize(const T value) noexcept
linearizes value
Definition core.h:3824
Tag for unit constructors.
Definition core.h:2217
Definition core.h:1238
Definition core.h:1123
Trait which tests whether a type is inherited from a decibel scale.
Definition core.h:3790
Trait which tests whether a type is inherited from a linear scale.
Definition core.h:3775
BinaryTypeTrait for querying whether Cf1 and Cf2 are conversion factors to the same dimension.
Definition core.h:2044
BinaryTypeTrait for querying whether U1 and U2 are units of the same dimension.
Definition core.h:2574
Traits which tests if a class is a unit.
Definition core.h:938
SFINAE-able trait which replaces the underlying type of Unit with Underlying.
Definition core.h:1501
SFINAE-able trait that maps a conversion_factor to its strengthened type.
Definition core.h:1076
Definition core.h:181
Definition core.h:175