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 angular_acceleration = dimension_divide<angular_velocity, time>;
1352 using angular_jerk = dimension_divide<angular_acceleration, time>;
1353 using force = dimension_multiply<mass, acceleration>;
1354 using area = dimension_pow<length, std::ratio<2>>;
1355 using volume = dimension_pow<length, std::ratio<3>>;
1356 using volume_flow_rate = dimension_divide<volume, time>;
1357 using pressure = dimension_divide<force, area>;
1358 using charge = dimension_multiply<time, current>;
1359 using energy = dimension_multiply<force, length>;
1360 using power = dimension_divide<energy, time>;
1361 using voltage = dimension_divide<power, current>;
1362 using capacitance = dimension_divide<charge, voltage>;
1363 using impedance = dimension_divide<voltage, current>;
1364 using conductance = dimension_divide<current, voltage>;
1365 using magnetic_flux = dimension_divide<energy, current>;
1366 using inductance = dimension_multiply<impedance, time>;
1367 using luminous_flux = dimension_multiply<solid_angle, luminous_intensity>;
1368 using illuminance = make_dimension<luminous_flux, std::ratio<1>, length, std::ratio<-2>>;
1369 using luminance = make_dimension<luminous_intensity, std::ratio<1>, length, std::ratio<-2>>;
1370 using radioactivity = make_dimension<length, std::ratio<2>, time, std::ratio<-2>>;
1371 using substance_mass = dimension_divide<mass, substance>;
1372 using substance_concentration = dimension_divide<substance, mass>;
1373 using magnetic_field_strength = make_dimension<mass, std::ratio<1>, time, std::ratio<-2>, current, std::ratio<-1>>;
1374 using radiant_intensity = make_dimension<power, std::ratio<1>, solid_angle, std::ratio<-1>>;
1375 using radiance = make_dimension<radiant_intensity, std::ratio<1>, area, std::ratio<-1>>;
1376 using irradiance = make_dimension<power, std::ratio<1>, area, std::ratio<-1>>;
1377 using spectral_intensity = make_dimension<radiant_intensity, std::ratio<1>, length, std::ratio<-1>>;
1378 using spectral_flux = make_dimension<power, std::ratio<1>, length, std::ratio<-1>>;
1379 using spectral_radiance = make_dimension<radiant_intensity, std::ratio<1>, volume, std::ratio<-1>>;
1380 using spectral_irradiance = make_dimension<power, std::ratio<1>, volume, std::ratio<-1>>;
1382 // OTHER UNIT TYPES
1383 using jerk = make_dimension<length, std::ratio<1>, time, std::ratio<-3>>;
1384 using torque = dimension_multiply<force, length>;
1385 using density = dimension_divide<mass, volume>;
1386 using dynamic_viscosity = dimension_multiply<pressure, time>;
1387 using kinematic_viscosity = dimension_divide<area, time>;
1388 using energy_density = make_dimension<energy, std::ratio<1>, volume, std::ratio<-1>>;
1389 using concentration = make_dimension<volume, std::ratio<-1>>;
1390 using data = make_dimension<data_tag>;
1391 using data_transfer_rate = dimension_divide<data, time>;
1392 } // namespace dimension
1393
1394 //------------------------------
1395 // CONVERSION FACTOR CLASSES
1396 //------------------------------
1397 // DOXYGEN IGNORE
1402 template<RatioType, class, RatioType, RatioType>
1403 struct conversion_factor;
1404
1405 template<RatioType Conversion, class... Exponents, RatioType PiExponent, RatioType Translation>
1406 struct conversion_factor<Conversion, dimension_t<Exponents...>, PiExponent, Translation> : detail::_conversion_factor
1407 {
1408 using dimension_type = dimension_t<Exponents...>;
1409 using conversion_ratio = Conversion;
1410 using translation_ratio = Translation;
1411 using pi_exponent_ratio = PiExponent;
1412 };
1413 // END DOXYGEN IGNORE
1415 // DOXYGEN IGNORE
1417 namespace detail
1418 {
1419 template<RatioType C, typename U, RatioType P, RatioType T>
1420 conversion_factor<C, U, P, T> conversion_factor_base_t_impl(conversion_factor<C, U, P, T>* cf)
1421 {
1422 return *cf;
1423 };
1424
1425 template<typename T>
1426 using conversion_factor_base_t = decltype(conversion_factor_base_t_impl(std::declval<T*>()));
1427
1434 template<class ConversionFactor>
1435 struct dimension_of_impl : dimension_of_impl<conversion_factor_base_t<ConversionFactor>>
1436 {
1437 };
1438
1439 template<RatioType Conversion, class BaseUnit, RatioType PiExponent, RatioType Translation>
1440 struct dimension_of_impl<conversion_factor<Conversion, BaseUnit, PiExponent, Translation>> : dimension_of_impl<BaseUnit>
1441 {
1442 };
1443
1444 template<class... Exponents>
1445 struct dimension_of_impl<dimension_t<Exponents...>>
1446 {
1447 using type = dimension_t<Exponents...>;
1448 };
1449
1450 template<>
1451 struct dimension_of_impl<void>
1452 {
1453 using type = void;
1454 };
1455 } // namespace detail // END DOXYGEN IGNORE
1458 namespace traits
1459 {
1465 template<class U>
1466 using dimension_of_t = typename units::detail::dimension_of_impl<U>::type;
1467 } // namespace traits
1468
1469 template<ConversionFactorType ConversionFactor, ArithmeticType T, NumericalScaleType<T> NumericalScale>
1470 class unit;
1471 // DOXYGEN IGNORE
1473 namespace detail
1474 {
1475 template<typename T, class Dim, bool IsConv = false>
1476 struct has_dimension_of_impl : std::false_type
1477 {
1478 };
1479
1480 template<typename T, class Dim>
1481 using has_dimension_of = typename has_dimension_of_impl<T, Dim, traits::is_conversion_factor_v<T>>::type;
1482
1483 template<typename Cf, class Dim>
1484 struct has_dimension_of_impl<Cf, Dim, true> : has_dimension_of<conversion_factor_base_t<Cf>, Dim>::type
1485 {
1486 };
1487
1488 template<typename C, typename Cf, typename P, typename T, class Dim>
1489 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
1490 {
1491 };
1492
1493 template<typename Cf, typename T, class Ns, class Dim>
1494 struct has_dimension_of_impl<unit<Cf, T, Ns>, Dim> : std::is_same<traits::dimension_of_t<Cf>, Dim>::type
1495 {
1496 };
1497 } // namespace detail // END DOXYGEN IGNORE
1499
1500 namespace traits
1501 {
1502 /**
1503 * @ingroup TypeTraits
1504 * @brief SFINAE-able trait which replaces the underlying type of `Unit` with `Underlying`.
1505 * @details If `Unit` is an unit, the member `type` alias names the same unit with an underlying type of
1506 * `Underlying`. Otherwise, there is no `type` member.
1507 * @param Unit The unit type whose underlying type is to be replaced.
1508 * @param Underlying The underlying type to replace that of `Unit`.
1509 */
1510 template<class, class>
1511 struct replace_underlying
1512 {
1513 };
1514
1515 template<ConversionFactorType Cf, ArithmeticType T, NumericalScaleType<T> Ns, ArithmeticType Underlying>
1516 struct replace_underlying<unit<Cf, T, Ns>, Underlying>
1518 using type = unit<Cf, Underlying, Ns>;
1519 };
1520
1521 template<class Unit, class Underlying>
1522 using replace_underlying_t = typename replace_underlying<Unit, Underlying>::type;
1523
1524 // True for dimensionless units whose conversion_ratio is not 1: percent, ppm, ppb, ppt, etc.
1525 template<class ConversionFactor, class = void>
1526 struct is_ratio_dimensionless_cf : std::false_type
1527 {
1528 };
1529
1530 template<class ConversionFactor>
1531 struct is_ratio_dimensionless_cf<ConversionFactor, std::void_t<typename ConversionFactor::dimension_type, typename ConversionFactor::conversion_ratio>>
1532 : std::bool_constant<std::is_same_v<typename ConversionFactor::dimension_type, dimension::dimensionless> && !std::ratio_equal_v<typename ConversionFactor::conversion_ratio, std::ratio<1>>>
1534 };
1535
1536 template<class ConversionFactor>
1537 inline constexpr bool is_ratio_dimensionless_cf_v = is_ratio_dimensionless_cf<ConversionFactor>::value;
1538
1539 } // namespace traits
1540
1541 template<typename U>
1542 concept RatioDimensionlessUnitType = units::DimensionlessUnitType<U> && traits::is_ratio_dimensionless_cf_v<typename U::conversion_factor>;
1543
1544 template<typename U>
1546
1547 template<typename U>
1548 concept IntegralUnitType = units::traits::is_unit_v<U> && std::integral<typename U::underlying_type>;
1549
1567 * `struct meters : conversion_factor<std::ratio<1>, units::dimension::length> {};`,
1568 * or type alias, i.e. `using inches = conversion_factor<std::ratio<1,12>, feet>`.
1569 * @tparam Conversion std::ratio representing dimensionless multiplication factor.
1570 * @tparam BaseUnit Unit type which this unit is derived from. May be a `dimension_t`, or another
1571 * `conversion_factor`.
1572 * @tparam PiExponent std::ratio representing the exponent of pi required by the conversion.
1573 * @tparam Translation std::ratio representing any datum translation required by the conversion.
1574 */
1575 template<RatioType Conversion, class BaseUnit, RatioType PiExponent = std::ratio<0>, RatioType Translation = std::ratio<0>>
1576 struct conversion_factor : detail::_conversion_factor
1577 {
1578 using dimension_type = traits::dimension_of_t<BaseUnit>;
1579 using conversion_ratio = std::ratio_multiply<typename BaseUnit::conversion_ratio, Conversion>;
1580 using pi_exponent_ratio = std::ratio_add<typename BaseUnit::pi_exponent_ratio, PiExponent>;
1581 using translation_ratio = std::ratio_add<std::ratio_multiply<typename BaseUnit::conversion_ratio, Translation>, typename BaseUnit::translation_ratio>;
1582 };
1583
1584 //------------------------------
1585 // UNIT MANIPULATORS
1586 //------------------------------
1587 // DOXYGEN IGNORE
1589 namespace detail
1590 {
1597 template<ConversionFactorType Cf1, ConversionFactorType Cf2>
1598 struct unit_multiply_impl
1599 {
1601 dimension_multiply<traits::dimension_of_t<typename Cf1::dimension_type>, traits::dimension_of_t<typename Cf2::dimension_type>>,
1602 std::ratio_add<typename Cf1::pi_exponent_ratio, typename Cf2::pi_exponent_ratio>>;
1603 };
1604
1609 template<ConversionFactorType Cf1, ConversionFactorType Cf2>
1610 using unit_multiply = typename unit_multiply_impl<Cf1, Cf2>::type;
1611
1618 template<ConversionFactorType Cf1, ConversionFactorType Cf2>
1619 struct unit_divide_impl
1620 {
1621 using type = conversion_factor<std::ratio_divide<typename Cf1::conversion_ratio, typename Cf2::conversion_ratio>,
1622 dimension_divide<traits::dimension_of_t<typename Cf1::dimension_type>, traits::dimension_of_t<typename Cf2::dimension_type>>,
1623 std::ratio_subtract<typename Cf1::pi_exponent_ratio, typename Cf2::pi_exponent_ratio>>;
1624 };
1625
1630 template<ConversionFactorType Cf1, ConversionFactorType Cf2>
1631 using unit_divide = typename unit_divide_impl<Cf1, Cf2>::type;
1632
1639 template<ConversionFactorType Cf>
1640 struct inverse_impl
1641 {
1642 using type = conversion_factor<std::ratio<Cf::conversion_ratio::den, Cf::conversion_ratio::num>, dimension_pow<typename Cf::dimension_type, std::ratio<-1>>,
1643 std::ratio_multiply<typename Cf::pi_exponent_ratio, std::ratio<-1>>>; // inverses are rates or changes, so translation factor is removed.
1644 };
1645 } // namespace detail // END DOXYGEN IGNORE
1647
1654 template<ConversionFactorType Cf>
1655 using inverse = typename detail::inverse_impl<Cf>::type;
1656 // DOXYGEN IGNORE
1658 namespace detail
1659 {
1666 template<ConversionFactorType Cf>
1667 struct squared_impl
1668 {
1669 using Conversion = typename Cf::conversion_ratio;
1670 using type = conversion_factor<std::ratio_multiply<Conversion, Conversion>, dimension_pow<traits::dimension_of_t<typename Cf::dimension_type>, std::ratio<2>>,
1671 std::ratio_multiply<typename Cf::pi_exponent_ratio, std::ratio<2>>, std::ratio<0>>;
1672 };
1673 } // namespace detail // END DOXYGEN IGNORE
1675
1682 template<ConversionFactorType Cf>
1683 using squared = typename detail::squared_impl<Cf>::type;
1684 // DOXYGEN IGNORE
1686 namespace detail
1687 {
1693 template<ConversionFactorType Cf>
1694 struct cubed_impl
1695 {
1696 using Conversion = typename Cf::conversion_ratio;
1698 dimension_pow<traits::dimension_of_t<typename Cf::dimension_type>, std::ratio<3>>, std::ratio_multiply<typename Cf::pi_exponent_ratio, std::ratio<3>>, std::ratio<0>>;
1699 };
1700 } // namespace detail // END DOXYGEN IGNORE
1702
1709 template<ConversionFactorType Cf>
1710 using cubed = typename detail::cubed_impl<Cf>::type;
1711 // DOXYGEN IGNORE
1713 // clang-format off
1714 namespace detail
1715 {
1716 //----------------------------------
1717 // RATIO_SQRT IMPLEMENTATION
1718 //----------------------------------
1719
1720 using Zero = std::ratio<0>;
1721 using One = std::ratio<1>;
1722 template <RatioType R> using Square = std::ratio_multiply<R, R>;
1723
1724 // Find the largest std::integer N such that Predicate<N>::value is true.
1725 template <template <std::intmax_t N> class Predicate, typename = void>
1726 struct BinarySearch
1727 {
1728 template <std::intmax_t N>
1729 struct SafeDouble_
1730 {
1731 static constexpr const std::intmax_t value = 2 * N;
1732 static_assert(value > 0, "Overflows when computing 2 * N");
1733 };
1734
1735 template <std::intmax_t Lower, std::intmax_t Upper, typename Condition1 = void, typename Condition2 = void>
1736 struct DoubleSidedSearch_ : DoubleSidedSearch_<Lower, Upper,
1737 std::integral_constant<bool, (Upper - Lower == 1)>,
1738 std::integral_constant<bool, ((Upper - Lower>1 && Predicate<Lower + (Upper - Lower) / 2>::value))>> {};
1739
1740 template <std::intmax_t Lower, std::intmax_t Upper>
1741 struct DoubleSidedSearch_<Lower, Upper, std::false_type, std::false_type> : DoubleSidedSearch_<Lower, Lower + (Upper - Lower) / 2> {};
1742
1743 template <std::intmax_t Lower, std::intmax_t Upper, typename Condition2>
1744 struct DoubleSidedSearch_<Lower, Upper, std::true_type, Condition2> : std::integral_constant<std::intmax_t, Lower>{};
1745
1746 template <std::intmax_t Lower, std::intmax_t Upper, typename Condition1>
1747 struct DoubleSidedSearch_<Lower, Upper, Condition1, std::true_type> : DoubleSidedSearch_<Lower + (Upper - Lower) / 2, Upper>{};
1748
1749 template <std::intmax_t Lower, class = void>
1750 struct SingleSidedSearch_ : SingleSidedSearch_<Lower, std::integral_constant<bool, Predicate<SafeDouble_<Lower>::value>::value>>{};
1751
1752 template <std::intmax_t Lower>
1753 struct SingleSidedSearch_<Lower, std::false_type> : DoubleSidedSearch_<Lower, SafeDouble_<Lower>::value> {};
1754
1755 template <std::intmax_t Lower>
1756 struct SingleSidedSearch_<Lower, std::true_type> : SingleSidedSearch_<SafeDouble_<Lower>::value>{};
1757
1758 static constexpr std::intmax_t value = SingleSidedSearch_<1>::value;
1759 };
1760
1761 template <template <std::intmax_t N> class Predicate>
1762 struct BinarySearch<Predicate, std::enable_if_t<!Predicate<1>::value>> : std::integral_constant<std::intmax_t, 0>{};
1763
1764 // Find largest std::integer N such that N<=sqrt(R)
1765 template <typename R>
1766 struct Integer
1767 {
1768 template <std::intmax_t N> using Predicate_ = std::ratio_less_equal<std::ratio<N>, std::ratio_divide<R, std::ratio<N>>>;
1769 static constexpr const std::intmax_t value = BinarySearch<Predicate_>::value;
1770 };
1771
1772 template <typename R>
1773 struct IsPerfectSquare
1774 {
1775 static constexpr const std::intmax_t DenSqrt_ = Integer<std::ratio<R::den>>::value;
1776 static constexpr const std::intmax_t NumSqrt_ = Integer<std::ratio<R::num>>::value;
1777 static constexpr const bool value =( DenSqrt_ * DenSqrt_ == R::den && NumSqrt_ * NumSqrt_ == R::num);
1778 using Sqrt = std::ratio<NumSqrt_, DenSqrt_>;
1779 };
1780
1781 // Represents sqrt(P)-Q.
1782 template <typename Tp, typename Tq>
1783 struct Remainder
1784 {
1785 using P = Tp;
1786 using Q = Tq;
1787 };
1788
1789 // Represents 1/R = I + Rem where R is a Remainder.
1790 template <typename R>
1791 struct Reciprocal
1792 {
1793 using P_ = typename R::P;
1794 using Q_ = typename R::Q;
1795 using Den_ = std::ratio_subtract<P_, Square<Q_>>;
1796 using A_ = std::ratio_divide<Q_, Den_>;
1797 using B_ = std::ratio_divide<P_, Square<Den_>>;
1798 static constexpr const std::intmax_t I_ = (A_::num + Integer<std::ratio_multiply<B_, Square<std::ratio<A_::den>>>>::value) / A_::den;
1799 using I = std::ratio<I_>;
1800 using Rem = Remainder<B_, std::ratio_subtract<I, A_>>;
1801 };
1802
1803 // Expands sqrt(R) to continued fraction:
1804 // f(x)=C1+1/(C2+1/(C3+1/(...+1/(Cn+x)))) = (U*x+V)/(W*x+1) and sqrt(R)=f(Rem).
1805 // The error |f(Rem)-V| = |(U-W*V)x/(W*x+1)| <= |U-W*V|*Rem <= |U-W*V|/I' where
1806 // I' is the std::integer part of reciprocal of Rem.
1807 template <typename Tr, std::intmax_t N>
1808 struct ContinuedFraction
1809 {
1810 template <typename T>
1811 using Abs_ = std::conditional_t<std::ratio_less_v<T, Zero>, std::ratio_subtract<Zero, T>, T>;
1812
1813 using R = Tr;
1814 using Last_ = ContinuedFraction<R, N - 1>;
1815 using Reciprocal_ = Reciprocal<typename Last_::Rem>;
1816 using Rem = typename Reciprocal_::Rem;
1817 using I_ = typename Reciprocal_::I;
1818 using Den_ = std::ratio_add<typename Last_::W, I_>;
1819 using U = std::ratio_divide<typename Last_::V, Den_>;
1820 using V = std::ratio_divide<std::ratio_add<typename Last_::U, std::ratio_multiply<typename Last_::V, I_>>, Den_>;
1821 using W = std::ratio_divide<One, Den_>;
1822 using Error = Abs_<std::ratio_divide<std::ratio_subtract<U, std::ratio_multiply<V, W>>, typename Reciprocal<Rem>::I>>;
1823 };
1824
1825 template <typename Tr>
1826 struct ContinuedFraction<Tr, 1>
1827 {
1828 using R = Tr;
1829 using U = One;
1830 using V = std::ratio<Integer<R>::value>;
1831 using W = Zero;
1832 using Rem = Remainder<R, V>;
1833 using Error = std::ratio_divide<One, typename Reciprocal<Rem>::I>;
1834 };
1835
1836 template <typename R, typename Eps, std::intmax_t N = 1, typename = void>
1837 struct Sqrt_ : Sqrt_<R, Eps, N + 1> {};
1838
1839 template <typename R, typename Eps, std::intmax_t N>
1840 struct Sqrt_<R, Eps, N, std::enable_if_t<std::ratio_less_equal_v<typename ContinuedFraction<R, N>::Error, Eps>>>
1841 {
1842 using type = typename ContinuedFraction<R, N>::V;
1843 };
1844
1845 template <typename R, typename, typename = void>
1846 struct Sqrt
1847 {
1848 static_assert(std::ratio_greater_equal_v<R, Zero>, "R can't be negative");
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>>
1853 {
1854 using type = typename IsPerfectSquare<R>::Sqrt;
1855 };
1856
1857 template <typename R, typename Eps>
1858 struct Sqrt<R, Eps, std::enable_if_t<(std::ratio_greater_equal_v<R, Zero> && !IsPerfectSquare<R>::value)>> : Sqrt_<R, Eps>{};
1859 }
1860 // clang-format on // END DOXYGEN IGNORE
1862
1883 template<RatioType Ratio, std::intmax_t Eps = 10000000000>
1884 using ratio_sqrt = typename units::detail::Sqrt<Ratio, std::ratio<1, Eps>>::type;
1885 // DOXYGEN IGNORE
1887 namespace detail
1888 {
1894 template<ConversionFactorType Unit, std::intmax_t Eps>
1895 struct sqrt_impl
1896 {
1897 using Conversion = typename Unit::conversion_ratio;
1898 using type = conversion_factor<ratio_sqrt<Conversion, Eps>, dimension_root<traits::dimension_of_t<typename Unit::dimension_type>, std::ratio<2>>,
1899 std::ratio_divide<typename Unit::pi_exponent_ratio, std::ratio<2>>, std::ratio<0>>;
1900 };
1901 } // namespace detail // END DOXYGEN IGNORE
1903
1925 template<ConversionFactorType Cf, std::intmax_t Eps = 10000000000>
1926 using square_root = typename detail::sqrt_impl<Cf, Eps>::type;
1927
1928 //------------------------------
1929 // COMPOUND UNITS
1930 //------------------------------
1931 // DOXYGEN IGNORE
1933 namespace detail
1934 {
1940 template<ConversionFactorType Cf, ConversionFactorType... Cfs>
1941 struct compound_impl;
1942
1943 template<ConversionFactorType Cf>
1944 struct compound_impl<Cf>
1945 {
1946 using type = Cf;
1947 };
1948
1950 struct compound_impl<Cf1, Cf2, Cfs...> : compound_impl<unit_multiply<Cf1, Cf2>, Cfs...>
1951 {
1952 };
1953 } // namespace detail // END DOXYGEN IGNORE
1955
1967 template<ConversionFactorType Cf, ConversionFactorType... Cfs>
1968 using compound_conversion_factor = typename detail::compound_impl<Cf, Cfs...>::type;
1969
1970 //------------------------------
1971 // PREFIXES
1972 //------------------------------
1973 // DOXYGEN IGNORE
1975 namespace detail
1976 {
1981 template<RatioType Ratio, ConversionFactorType ConversionFactor>
1982 struct prefix
1983 {
1985 };
1986
1988 template<int N, RatioType R>
1989 struct power_of_ratio
1990 {
1991 using type = std::ratio_multiply<R, typename power_of_ratio<N - 1, R>::type>;
1992 };
1993
1995 template<RatioType R>
1996 struct power_of_ratio<1, R>
1997 {
1998 using type = R;
1999 };
2000 } // namespace detail // END DOXYGEN IGNORE
2003 // clang-format off
2008 template<ConversionFactorType Cf> using atto = typename detail::prefix<std::atto,Cf>::type;
2009 template<ConversionFactorType Cf> using femto = typename detail::prefix<std::femto,Cf>::type;
2010 template<ConversionFactorType Cf> using pico = typename detail::prefix<std::pico,Cf>::type;
2011 template<ConversionFactorType Cf> using nano = typename detail::prefix<std::nano,Cf>::type;
2012 template<ConversionFactorType Cf> using micro = typename detail::prefix<std::micro,Cf>::type;
2013 template<ConversionFactorType Cf> using milli = typename detail::prefix<std::milli,Cf>::type;
2014 template<ConversionFactorType Cf> using centi = typename detail::prefix<std::centi,Cf>::type;
2015 template<ConversionFactorType Cf> using deci = typename detail::prefix<std::deci,Cf>::type;
2016 template<ConversionFactorType Cf> using deca = typename detail::prefix<std::deca,Cf>::type;
2017 template<ConversionFactorType Cf> using hecto = typename detail::prefix<std::hecto,Cf>::type;
2018 template<ConversionFactorType Cf> using kilo = typename detail::prefix<std::kilo,Cf>::type;
2019 template<ConversionFactorType Cf> using mega = typename detail::prefix<std::mega,Cf>::type;
2020 template<ConversionFactorType Cf> using giga = typename detail::prefix<std::giga,Cf>::type;
2021 template<ConversionFactorType Cf> using tera = typename detail::prefix<std::tera,Cf>::type;
2022 template<ConversionFactorType Cf> using peta = typename detail::prefix<std::peta,Cf>::type;
2023 template<ConversionFactorType Cf> using exa = typename detail::prefix<std::exa, Cf>::type;
2030 template<ConversionFactorType Cf> using kibi = typename detail::prefix<std::ratio<1024>, Cf>::type;
2031 template<ConversionFactorType Cf> using mebi = typename detail::prefix<std::ratio<1048576>, Cf>::type;
2032 template<ConversionFactorType Cf> using gibi = typename detail::prefix<std::ratio<1073741824>, Cf>::type;
2033 template<ConversionFactorType Cf> using tebi = typename detail::prefix<std::ratio<1099511627776>, Cf>::type;
2034 template<ConversionFactorType Cf> using pebi = typename detail::prefix<std::ratio<1125899906842624>, Cf>::type;
2035 template<ConversionFactorType Cf> using exbi = typename detail::prefix<std::ratio<1152921504606846976>, Cf>::type;
2037 // clang-format on
2038
2039 //------------------------------
2040 // CONVERSION TRAITS
2041 //------------------------------
2042
2043 namespace traits
2044 {
2048 * are conversion factors to the same dimension.
2049 * @details The base characteristic is a specialization of the template `std::bool_constant`.
2050 * Use `is_same_dimension_conversion_factor_v<Cf1, Cf2>` to test whether `Cf1` and `Cf2`
2051 * are conversion factors to the same dimension.
2052 * @tparam Cf1 Conversion factor to query.
2053 * @tparam Cf2 Conversion factor to query.
2054 * @sa is_same_dimension_unit
2055 */
2056 template<ConversionFactorType Cf1, ConversionFactorType Cf2>
2058 : 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>>>
2059 {
2060 };
2061
2062 template<ConversionFactorType Cf1, ConversionFactorType Cf2>
2063 inline constexpr bool is_same_dimension_conversion_factor_v = is_same_dimension_conversion_factor<Cf1, Cf2>::value;
2072 template<ConversionFactorType Cf>
2073 inline constexpr bool is_affine_conversion_factor_v = !std::ratio_equal_v<typename conversion_factor_traits<Cf>::translation_ratio, std::ratio<0>>;
2074 } // namespace traits
2075
2076 //------------------------------
2077 // CONSTEXPR MATH FUNCTIONS
2078 //------------------------------
2079 // DOXYGEN IGNORE
2081 namespace detail
2082 {
2089 template<typename T>
2090 struct floating_point_promotion : std::conditional<std::is_floating_point_v<T>, T, double>
2091 {
2092 };
2093
2094 template<typename T>
2095 using floating_point_promotion_t = typename floating_point_promotion<T>::type;
2096
2097 template<ConversionFactorType Cf, typename T, class Ns>
2098 struct floating_point_promotion<unit<Cf, T, Ns>>
2099 {
2100 using type = unit<Cf, floating_point_promotion_t<T>, Ns>;
2101 };
2102
2114 template<class To, class From>
2115 constexpr To exact_integral_cast(From value)
2116 {
2117 const To result = static_cast<To>(value);
2118 if (static_cast<From>(result) != value)
2119 throw "a floating-point unit converts to an integral unit only when its value is a whole number in range";
2120 return result;
2121 }
2122 } // namespace detail
2123
2124 namespace Detail
2125 {
2126 template<std::floating_point T>
2127 constexpr T sqrtNewtonRaphson(T x, T curr, T prev)
2128 {
2129 return curr == prev ? curr : sqrtNewtonRaphson(x, T{0.5} * (curr + x / curr), curr);
2130 }
2131 } // namespace Detail // END DOXYGEN IGNORE
2133
2134 template<ArithmeticType T>
2135 constexpr detail::floating_point_promotion_t<T> sqrt(T x_)
2136 {
2137 using FloatingPoint = detail::floating_point_promotion_t<T>;
2138
2139 const FloatingPoint x(x_);
2140
2141 return x >= 0 && x < std::numeric_limits<FloatingPoint>::infinity() ? Detail::sqrtNewtonRaphson(x, x, FloatingPoint(0)) : std::numeric_limits<FloatingPoint>::quiet_NaN();
2142 }
2143 // DOXYGEN IGNORE
2145 namespace detail
2146 {
2147 template<unsigned long long Exp, typename B>
2148 constexpr auto pow_acc(B acc, B base [[maybe_unused]]) noexcept
2149 {
2150 if constexpr (Exp == 0)
2151 {
2152 return static_cast<B>(acc);
2153 }
2154 else if constexpr ((Exp & 1) == 0)
2155 {
2156 return pow_acc<Exp / 2>(acc, base * base);
2157 }
2158 else
2159 {
2160 return pow_acc<(Exp - 1) / 2>(acc * base, base * base);
2161 }
2162 }
2163 } // namespace detail // END DOXYGEN IGNORE
2165
2166 template<signed long long Exp, ArithmeticType B>
2167 constexpr detail::floating_point_promotion_t<B> pow(B base) noexcept
2168 {
2169 using promoted_t = detail::floating_point_promotion_t<B>;
2170 constexpr auto one = static_cast<promoted_t>(1);
2171 if constexpr (Exp >= 0)
2172 {
2173 return detail::pow_acc<Exp>(one, static_cast<promoted_t>(base));
2174 }
2175 constexpr auto new_exp = static_cast<unsigned long long>(-(Exp + 1));
2176 return 1 / (base * detail::pow_acc<new_exp>(one, static_cast<promoted_t>(base)));
2177 }
2178 // DOXYGEN IGNORE
2180 namespace detail
2181 {
2182 template<typename T1, typename T2>
2183 constexpr auto pow_acc(T1 acc, T1 x, T2 y) noexcept
2184 {
2185 if (y == 0)
2186 {
2187 return acc;
2188 }
2189 if (y % 2 == 0)
2190 {
2191 return pow_acc(acc, x * x, y / 2);
2192 }
2193 return pow_acc(acc * x, x * x, (y - 1) / 2);
2194 }
2195 } // namespace detail // END DOXYGEN IGNORE
2197
2198 template<ArithmeticType T1, ArithmeticType T2>
2199 requires std::is_unsigned_v<T2>
2200 constexpr detail::floating_point_promotion_t<T1> pow(T1 x, T2 y) noexcept
2201 {
2202 using promoted_t = detail::floating_point_promotion_t<T1>;
2203 return detail::pow_acc(static_cast<promoted_t>(1.0), static_cast<promoted_t>(x), y);
2204 }
2205
2206 template<ArithmeticType T1, ArithmeticType T2>
2207 requires std::is_signed_v<T2>
2208 constexpr detail::floating_point_promotion_t<T1> pow(T1 x, T2 y) noexcept
2209 {
2210 if (y >= 0)
2211 {
2212 return pow(x, static_cast<unsigned long long>(y));
2213 }
2214 return 1 / (x * pow(x, static_cast<unsigned long long>(-(y + 1))));
2215 }
2216
2217 template<ArithmeticType T>
2218 constexpr T abs(T x)
2219 {
2220 return x < 0 ? -x : x;
2221 }
2223 //------------------------------
2224 // CONVERSION FUNCTIONS
2225 //------------------------------
2226
2231 struct linearized_value_t
2232 {
2233 explicit linearized_value_t() = default;
2234 };
2235
2236 inline constexpr linearized_value_t linearized_value{};
2237 // DOXYGEN IGNORE
2239 namespace detail
2240 {
2246#if defined(__SIZEOF_INT128__)
2247 using widest_signed_int = __int128;
2248 using widest_unsigned_int = unsigned __int128;
2249 inline constexpr bool has_builtin_int128 = true;
2250#else
2251 using widest_signed_int = std::intmax_t;
2252 using widest_unsigned_int = std::uintmax_t;
2253 inline constexpr bool has_builtin_int128 = false;
2254#endif
2255
2260 template<class Rep>
2261 constexpr Rep widening_mul_div(Rep value, std::intmax_t num, std::intmax_t den) noexcept
2262 {
2263 if constexpr (has_builtin_int128)
2264 {
2265 return static_cast<Rep>(static_cast<widest_signed_int>(value) * static_cast<widest_signed_int>(num) / static_cast<widest_signed_int>(den));
2266 }
2267 else
2268 {
2269 // Sign-separated 64x64->128 multiply, then 128/64 divide, all in unsigned 64-bit limbs so no
2270 // intermediate exceeds the representable range. `num`/`den` are positive (a std::ratio is stored in
2271 // lowest terms with a positive denominator); only `value` may be negative.
2272 const bool negative = (value < 0);
2273 const std::uint64_t a = negative ? static_cast<std::uint64_t>(-(value + 1)) + 1u : static_cast<std::uint64_t>(value);
2274 const std::uint64_t b = static_cast<std::uint64_t>(num);
2275 const std::uint64_t d = static_cast<std::uint64_t>(den);
2276
2277 // 64x64 -> 128 as two 64-bit limbs (hi, lo).
2278 const std::uint64_t aLo = a & 0xFFFFFFFFull, aHi = a >> 32;
2279 const std::uint64_t bLo = b & 0xFFFFFFFFull, bHi = b >> 32;
2280 const std::uint64_t ll = aLo * bLo;
2281 const std::uint64_t lh = aLo * bHi;
2282 const std::uint64_t hl = aHi * bLo;
2283 const std::uint64_t hh = aHi * bHi;
2284 const std::uint64_t cross = (ll >> 32) + (lh & 0xFFFFFFFFull) + (hl & 0xFFFFFFFFull);
2285 std::uint64_t hi = hh + (lh >> 32) + (hl >> 32) + (cross >> 32);
2286 std::uint64_t lo = (cross << 32) | (ll & 0xFFFFFFFFull);
2287
2288 // 128 (hi:lo) / d -> long division of the two limbs by a 64-bit divisor.
2289 std::uint64_t quotient = 0;
2290 std::uint64_t rem = 0;
2291 for (int bit = 127; bit >= 0; --bit)
2292 {
2293 rem = (rem << 1) | ((bit >= 64 ? (hi >> (bit - 64)) : (lo >> bit)) & 1u);
2294 const bool canSubtract = (rem >= d);
2295 rem -= canSubtract ? d : 0u;
2296 if (bit < 64)
2297 quotient |= (static_cast<std::uint64_t>(canSubtract) << bit);
2298 }
2299 const auto result = static_cast<Rep>(quotient);
2300 return negative ? static_cast<Rep>(-result) : result;
2301 }
2302 }
2303
2310 template<class Rep>
2311 constexpr bool integral_conversion_is_exact(Rep value, std::intmax_t num, std::intmax_t den) noexcept
2312 {
2313 const widest_signed_int product = static_cast<widest_signed_int>(value) * static_cast<widest_signed_int>(num);
2314 return product % static_cast<widest_signed_int>(den) == 0;
2315 }
2316
2329 template<class To, class From>
2330 constexpr To exact_integral_unit_cast(From value, std::intmax_t num, std::intmax_t den)
2331 {
2332 if (!integral_conversion_is_exact(value, num, den))
2333 throw "an integral unit converts to a coarser integral unit only when the value is an exact whole number of the target unit";
2334 return static_cast<To>(widening_mul_div(value, num, den));
2335 }
2336 } // namespace detail // END DOXYGEN IGNORE
2338
2351 * @tparam From type of <i>value</i>. Shall be an arithmetic type.
2352 * @param[in] value Arithmetic value to convert.
2353 * The value should represent a quantity in units of `ConversionFactorFrom`.
2354 * @tparam To type of the converted unit value. Shall be an arithmetic type.
2355 * @returns value, converted from units of `ConversionFactorFrom` to `ConversionFactorTo`.
2356 * The value represents a quantity in units of `ConversionFactorTo`.
2357 */
2358 template<ConversionFactorType ConversionFactorFrom, ConversionFactorType ConversionFactorTo, ArithmeticType To = UNIT_LIB_DEFAULT_TYPE, ArithmeticType From>
2359 requires(traits::is_same_dimension_conversion_factor_v<ConversionFactorFrom, ConversionFactorTo>)
2360 constexpr To convert(const From& value) noexcept
2361 {
2362 using Ratio = std::ratio_divide<typename ConversionFactorFrom::conversion_ratio, typename ConversionFactorTo::conversion_ratio>;
2363 using PiRatio = std::ratio_subtract<typename ConversionFactorFrom::pi_exponent_ratio, typename ConversionFactorTo::pi_exponent_ratio>;
2364 using Translation =
2365 std::ratio_divide<std::ratio_subtract<typename ConversionFactorFrom::translation_ratio, typename ConversionFactorTo::translation_ratio>, typename ConversionFactorTo::conversion_ratio>;
2366
2367 [[maybe_unused]] constexpr auto normal_convert = []<typename T0>(const T0& val)
2368 {
2372 };
2373
2374 [[maybe_unused]] constexpr auto pi_convert = []<typename T0>(const T0& val)
2375 {
2376 using ResolvedUnitFrom =
2380 };
2381
2382 // same exact unit on both sides
2383 if constexpr (std::same_as<ConversionFactorFrom, ConversionFactorTo>)
2384 {
2385 return static_cast<To>(value);
2386 }
2387 // PI REQUIRED, no translation
2388 else if constexpr (!std::same_as<std::ratio<0>, PiRatio> && std::same_as<std::ratio<0>, Translation>)
2389 {
2390 using CommonUnderlying = std::common_type_t<To, From, UNIT_LIB_DEFAULT_TYPE>;
2391 // The pi exponent as a real number. Compute in long double: PiRatio::num/PiRatio::den are
2392 // intmax_t, so an integer division here would truncate a fractional exponent (e.g. ratio<1,2>
2393 // -> 0), which both corrupts the value and, for the fractional case, produced a non-constant
2394 // expression / missing-return compile error.
2395 constexpr long double PiRatioValue = static_cast<long double>(PiRatio::num) / static_cast<long double>(PiRatio::den);
2396 constexpr bool integerExponent = (PiRatio::num % PiRatio::den == 0);
2397
2398 // A whole-number exponent uses the constexpr integer `pow`; a fractional exponent needs
2399 // `std::pow` (not constant-evaluable), so that sole case degrades to a run-time computation.
2400 if constexpr (integerExponent && PiRatioValue >= 0)
2401 {
2402 return static_cast<To>(normal_convert(static_cast<CommonUnderlying>(value) * static_cast<CommonUnderlying>(pow(detail::PI_VAL, PiRatioValue))));
2403 }
2404 else if constexpr (integerExponent) // PiRatioValue < 0
2405 {
2406 return static_cast<To>(normal_convert(static_cast<CommonUnderlying>(value) / static_cast<CommonUnderlying>(pow(detail::PI_VAL, -PiRatioValue))));
2407 }
2408 else // fractional exponent (either sign): std::pow handles both directions
2409 {
2410 return static_cast<To>(normal_convert(static_cast<CommonUnderlying>(value) * static_cast<CommonUnderlying>(std::pow(detail::PI_VAL, PiRatioValue))));
2411 }
2412 }
2413 // Translation required, no pi variable
2414 else if constexpr (std::same_as<std::ratio<0>, PiRatio> && !std::same_as<std::ratio<0>, Translation>)
2415 {
2416 using CommonUnderlying = std::common_type_t<To, From, UNIT_LIB_DEFAULT_TYPE>;
2417
2418 return static_cast<To>(normal_convert(static_cast<CommonUnderlying>(value)) + (static_cast<CommonUnderlying>(Translation::num) / static_cast<CommonUnderlying>(Translation::den)));
2419 }
2420 // pi and translation needed
2421 else if constexpr (!std::same_as<std::ratio<0>, PiRatio> && !std::same_as<std::ratio<0>, Translation>)
2422 {
2423 using CommonUnderlying = std::common_type_t<To, From, UNIT_LIB_DEFAULT_TYPE>;
2424
2425 return static_cast<To>(pi_convert(static_cast<CommonUnderlying>(value)) + (static_cast<CommonUnderlying>(Translation::num) / static_cast<CommonUnderlying>(Translation::den)));
2426 }
2427 // normal conversion between two different units
2428 else
2429 {
2430 using CommonUnderlying = std::common_type_t<To, From, std::intmax_t>;
2431
2432 if constexpr (Ratio::num == 1 && Ratio::den == 1)
2433 return static_cast<To>(value);
2434 if constexpr (Ratio::num != 1 && Ratio::den == 1)
2435 return static_cast<To>(static_cast<CommonUnderlying>(value) * static_cast<CommonUnderlying>(Ratio::num));
2436 if constexpr (Ratio::num == 1 && Ratio::den != 1)
2437 return static_cast<To>(static_cast<CommonUnderlying>(value) / static_cast<CommonUnderlying>(Ratio::den));
2438 if constexpr (Ratio::num != 1 && Ratio::den != 1)
2439 {
2440 // A mul-then-divide conversion. The goal is the MOST accurate representable result:
2441 // - Integral intermediate: carry `value * num` in a double-width integer so it cannot overflow
2442 // before `/ den` recovers a value that fits the target (no wrong answer, no precision lost).
2443 // - Floating-point: `(value * num) / den` is the most accurate order (a single rounding) and is
2444 // used whenever `value * num` is representable. Only when that product would overflow to
2445 // infinity — a blatantly wrong answer where a finite result exists — fall back to the
2446 // divide-first order `value / den * num`, which trades a little rounding for a representable
2447 // answer. Normal-magnitude conversions therefore keep the correctly-rounded mul-then-divide.
2448 if constexpr (std::is_integral_v<CommonUnderlying>)
2449 {
2450 return static_cast<To>(detail::widening_mul_div(static_cast<CommonUnderlying>(value), Ratio::num, Ratio::den));
2451 }
2452 else
2453 {
2454 const CommonUnderlying v = static_cast<CommonUnderlying>(value);
2455 const CommonUnderlying num = static_cast<CommonUnderlying>(Ratio::num);
2456 const CommonUnderlying den = static_cast<CommonUnderlying>(Ratio::den);
2457 // `value * num` overflows the type when |value| exceeds max / num. Guard on that exact threshold
2458 // so the lossy divide-first path is taken ONLY when the accurate path would produce infinity.
2459 const CommonUnderlying limit = (std::numeric_limits<CommonUnderlying>::max)() / num;
2460 if (v > limit || v < -limit)
2461 return static_cast<To>((v / den) * num);
2462 return static_cast<To>((v * num) / den);
2463 }
2464 }
2465 }
2466 }
2467 // DOXYGEN IGNORE
2469 namespace detail
2470 {
2476 template<UnitType UnitFrom, UnitType UnitTo>
2477 struct delayed_is_same_dimension_conversion_factor : std::false_type
2478 {
2479 static constexpr bool value = traits::is_same_dimension_conversion_factor_v<typename UnitFrom::conversion_factor, typename UnitTo::conversion_factor>;
2480 };
2481 } // namespace detail // END DOXYGEN IGNORE
2483
2489 * computations are carried in the widest representation before being converted to `UnitTo`.
2490 * `is_same_dimension_unit_v<UnitFrom, UnitTo>` shall be `true`.
2491 * @sa unit for implicit conversion of unit containers.
2492 * @tparam UnitFrom unit to convert to `UnitTo`. `is_unit_v<UnitFrom>` shall be `true`.
2493 * @tparam UnitTo unit to convert `from` to. `is_unit_v<UnitTo>` shall be `true`.
2494 * @returns from, converted from units of `UnitFrom` to `UnitTo`.
2495 */
2496 template<UnitType UnitTo, UnitType UnitFrom>
2498 constexpr UnitTo convert(const UnitFrom& from) noexcept
2499 {
2501 }
2502
2503 //------------------------------
2504 // UNIT TYPE TRAITS
2505 //------------------------------
2506
2507 namespace traits
2508 {
2509#ifdef FOR_DOXYGEN_PURPOSOES_ONLY
2516 template<typename T>
2517 struct unit_traits
2518 {
2519 typedef typename T::numerical_scale_type numerical_scale_type;
2522 typedef typename T::underlying_type underlying_type;
2523 typedef typename T::value_type value_type;
2524 typedef typename T::conversion_factor conversion_factor;
2525 };
2526#endif
2527 // DOXYGEN IGNORE
2533 template<typename, typename = void>
2534 struct unit_traits
2535 {
2536 using numerical_scale_type = void;
2537 using underlying_type = void;
2538 using value_type = void;
2539 using conversion_factor = void;
2540 };
2541
2542 template<ArithmeticType T>
2543 struct unit_traits<T, std::void_t<T>>
2544 {
2545 using numerical_scale_type = void;
2546 using underlying_type = T;
2547 using value_type = void;
2548 using conversion_factor = units::conversion_factor<std::ratio<1>, dimension_t<>>;
2549 };
2550
2556 template<NonArithmeticType T>
2557 struct unit_traits<T, std::void_t<typename T::numerical_scale_type, typename T::underlying_type, typename T::value_type, typename T::conversion_factor>>
2558 {
2559 using numerical_scale_type = typename T::numerical_scale_type;
2560 using underlying_type = typename T::underlying_type;
2561 using value_type = typename T::value_type;
2562 using conversion_factor = typename T::conversion_factor;
2563 };
2564 // END DOXYGEN IGNORE
2566 } // namespace traits
2567
2568 namespace traits
2569 {
2573 template<UnitType U>
2574 inline constexpr bool is_affine_unit_v = is_affine_conversion_factor_v<typename unit_traits<U>::conversion_factor>;
2575
2578 * @brief `BinaryTypeTrait` for querying whether `U1` and `U2` are units of the same dimension.
2579 * @details The base characteristic is a specialization of the template `std::bool_constant`.
2580 * Use `is_same_dimension_unit_v<U1, U2>` to test whether `U1` and `U2`
2581 * are units of the same dimension.
2582 * @tparam U1 Unit to query.
2583 * @tparam U2 Unit to query.
2584 * @sa is_same_dimension_conversion_factor
2585 */
2586 template<UnitType U1, UnitType U2>
2588 : 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>>
2589 {
2590 };
2591
2592 template<UnitType U1, UnitType U2>
2593 inline constexpr bool is_same_dimension_unit_v = is_same_dimension_unit<U1, U2>::value;
2594 } // namespace traits
2595
2596 //----------------------------------
2597 // UNIT TYPE
2598 //----------------------------------
2599 // DOXYGEN IGNORE
2601
2602 namespace detail
2603 {
2604 // Forward declaration so unit's name()/abbreviation() members (defined below, in the unit class) can name
2605 // detail::rewrap_to_named_t; the full definition follows after the unit class is complete (it depends on it).
2606 template<class U, class = void>
2607 struct rewrap_to_named;
2608 template<class U>
2609 using rewrap_to_named_t = typename rewrap_to_named<U>::type;
2610
2614 template<class From, class To>
2615 inline constexpr bool is_losslessly_convertible = std::is_arithmetic_v<From> && (std::is_floating_point_v<To> || !std::is_floating_point_v<From>);
2616
2621 template<ConversionFactorType ConversionFactorFrom, ConversionFactorType ConversionFactorTo>
2622 struct is_non_truncated_convertible_unit : std::false_type
2623 {
2624 static constexpr bool value = std::ratio_divide<typename ConversionFactorFrom::conversion_ratio, typename ConversionFactorTo::conversion_ratio>::den == 1;
2625 };
2626
2630 template<class UnitFrom, class UnitTo>
2631 inline constexpr bool is_losslessly_convertible_unit = std::conjunction_v<traits::is_same_dimension_unit<UnitFrom, UnitTo>,
2632 std::disjunction<std::is_floating_point<typename UnitTo::underlying_type>,
2633 std::conjunction<std::negation<std::is_floating_point<typename UnitFrom::underlying_type>>,
2634 is_non_truncated_convertible_unit<typename UnitFrom::conversion_factor, typename UnitTo::conversion_factor>>>>;
2635
2637 template<class L, class R>
2638 inline constexpr bool both_floating_v = std::is_floating_point_v<typename traits::unit_traits<L>::underlying_type> &&
2639 std::is_floating_point_v<typename traits::unit_traits<R>::underlying_type>;
2640
2647 template<class L, class R>
2648 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>>;
2649
2650 // The underlying type a NAMED unit's from-unit deduction guide should produce when constructed from `Source`:
2651 // the source's own underlying when losslessly convertible into the target (StrongCf, Scale), else its
2652 // floating-point promotion (so e.g. radians(degrees{1}) deduces radians<double>). A SFINAE-friendly class
2653 // template (NOT a var-template init), so the guide's return type never eagerly instantiates
2654 // is_losslessly_convertible_unit for a non-unit / non-same-dimension Source — the primary is chosen and the
2655 // heavy check only runs in the partial specialization, which is constrained to a same-dimension unit source.
2656 template<class Source, class StrongCf, class Scale, class = void>
2657 struct deduced_named_underlying
2658 {
2659 using type = typename traits::unit_traits<Source>::underlying_type;
2660 };
2661 template<class Source, class StrongCf, class Scale>
2662 struct deduced_named_underlying<Source, StrongCf, Scale,
2663 std::enable_if_t<traits::is_unit_v<Source> &&
2664 traits::is_same_dimension_unit_v<Source, unit<StrongCf, typename traits::unit_traits<Source>::underlying_type, Scale>>>>
2665 {
2666 private:
2667 using Src = typename traits::unit_traits<Source>::underlying_type;
2668
2669 public:
2670 using type = std::conditional_t<is_losslessly_convertible_unit<Source, unit<StrongCf, Src, Scale>>, Src, floating_point_promotion_t<Src>>;
2671 };
2672 template<class Source, class StrongCf, class Scale>
2673 using deduced_named_underlying_t = typename deduced_named_underlying<Source, StrongCf, Scale>::type;
2674
2675 template<RatioType Ratio>
2676 using time_conversion_factor = conversion_factor<Ratio, dimension::time>;
2677
2681 template<ConversionFactorType ConversionFactor>
2682 inline constexpr bool is_time_conversion_factor = traits::is_same_dimension_conversion_factor_v<ConversionFactor, time_conversion_factor<std::ratio<1>>>;
2683 } // namespace detail // END DOXYGEN IGNORE
2685
2741#ifdef _WIN32
2742 // Microsoft compiler requires explicit activation of empty base class optimization
2743 // so that sizeof(unit<..., double, ...>) == sizeof(double)
2744#define MSVC_EBO __declspec(empty_bases)
2745#else
2746#define MSVC_EBO
2747#endif
2748 template<ConversionFactorType ConversionFactor, ArithmeticType T = UNIT_LIB_DEFAULT_TYPE, NumericalScaleType<T> NumericalScale = linear_scale>
2749 class MSVC_EBO unit : public ConversionFactor, public NumericalScale, public detail::_unit
2750 {
2751 public:
2752 using numerical_scale_type = NumericalScale;
2753 using underlying_type = T;
2754 using value_type = T;
2755 using conversion_factor = ConversionFactor;
2756
2761 constexpr unit() = default;
2762
2767 constexpr unit(const unit&) = default;
2768
2771
2774 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2775 requires traits::is_same_dimension_unit_v<unit<ConversionFactorRhs, Ty, NsRhs>, unit> && detail::is_losslessly_convertible_unit<unit<ConversionFactorRhs, Ty, NsRhs>, unit>
2776 constexpr unit(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) noexcept
2778 {
2779 }
2780
2788 * run-time floating-to-integral unit conversion remains rejected. Wholeness is judged on the
2789 * stored point count (`raw()`), so a ratio-dimensionless unit converts correctly too
2790 * (`percent<int> p = 1_pct;` is percent<int> holding 1, not a rejected 0.01).
2791 * @param[in] rhs unit to convert.
2792 */
2793 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2794 requires(traits::is_same_dimension_unit_v<unit<ConversionFactorRhs, Ty, NsRhs>, unit> &&
2795 !detail::is_losslessly_convertible_unit<unit<ConversionFactorRhs, Ty, NsRhs>, unit> &&
2796 std::is_floating_point_v<Ty> && std::is_integral_v<T>)
2797 consteval unit(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs)
2798 : _linearized_value(detail::exact_integral_cast<T>(unit<ConversionFactor, detail::floating_point_promotion_t<T>, NumericalScale>(rhs).raw()))
2799 {
2800 }
2801
2811 * arithmetic in a double-width intermediate, so it cannot be defeated by an intermediate overflow.
2812 * A run-time integral-to-coarser-integral unit conversion remains rejected; use `round`/`floor`/
2813 * `ceil`/`trunc<To>` for a deliberate run-time rounding.
2814 * @param[in] rhs unit to convert.
2815 */
2816 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2817 requires(traits::is_same_dimension_unit_v<unit<ConversionFactorRhs, Ty, NsRhs>, unit> &&
2818 !detail::is_losslessly_convertible_unit<unit<ConversionFactorRhs, Ty, NsRhs>, unit> &&
2819 std::is_integral_v<Ty> && std::is_integral_v<T>)
2820 consteval unit(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs)
2821 : _linearized_value(detail::exact_integral_unit_cast<T>(rhs.raw(),
2822 std::ratio_divide<typename ConversionFactorRhs::conversion_ratio, typename ConversionFactor::conversion_ratio>::num,
2823 std::ratio_divide<typename ConversionFactorRhs::conversion_ratio, typename ConversionFactor::conversion_ratio>::den))
2824 {
2826
2829
2832 template<ArithmeticType Ty>
2833 requires(!traits::is_dimensionless_unit<ConversionFactor>::value && detail::is_losslessly_convertible<Ty, T>)
2834 explicit constexpr unit(Ty value) noexcept
2835 : _linearized_value(NumericalScale::linearize(static_cast<T>(value)))
2836 {
2838
2841
2844 template<ArithmeticType Ty>
2845 requires detail::is_losslessly_convertible<Ty, T>
2846 explicit constexpr unit(Ty value, linearized_value_t) noexcept
2847 : _linearized_value(value)
2848 {
2850
2853
2856 template<ArithmeticType Ty>
2857 requires traits::is_dimensionless_unit<ConversionFactor>::value && detail::is_losslessly_convertible<Ty, T>
2858 constexpr unit(Ty value) noexcept
2859 : _linearized_value(NumericalScale::linearize(static_cast<T>(value)))
2860 {
2861 }
2866
2868 template<ArithmeticType Rep, RatioType Period>
2869 requires detail::is_time_conversion_factor<ConversionFactor> && detail::is_losslessly_convertible<Rep, T> &&
2870 detail::is_losslessly_convertible_unit<units::unit<units::conversion_factor<Period, dimension::time>, Rep>, unit>
2871 constexpr unit(const std::chrono::duration<Rep, Period>& value) noexcept
2873 {
2874 }
2875
2881 constexpr unit& operator=(const unit& rhs) noexcept = default;
2882
2887
2888 template<ConversionFactorType Cf = ConversionFactor>
2890 constexpr unit& operator=(const underlying_type& rhs) noexcept
2891 {
2892 unit<units::conversion_factor<std::ratio<1>, units::dimension::dimensionless>, underlying_type, linear_scale> dimensionlessRhs(rhs);
2893 _linearized_value = units::convert<unit>(dimensionlessRhs)._linearized_value;
2894 return *this;
2896
2899
2903 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2904 constexpr bool operator<(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2905 {
2906 return value_compare(rhs) < 0;
2908
2911
2915 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2916 constexpr bool operator<=(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2917 {
2918 return value_compare(rhs) <= 0;
2920
2923
2927 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2928 constexpr bool operator>(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2929 {
2930 return value_compare(rhs) > 0;
2932
2935
2939 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2940 constexpr bool operator>=(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2941 {
2942 return value_compare(rhs) >= 0;
2943 }
2944
2945 /**
2946 * @brief equality
2947 * @details compares the linearized value of two units. Performs unit conversions if necessary.
2948 * @param[in] rhs right-hand side unit for the comparison
2949 * @returns true IFF the value of `this` exactly equal to the value of rhs.
2950 * @note This may not be suitable for all applications when the underlying_type of unit is a double.
2951 */
2952 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2953 requires(std::floating_point<T> || std::floating_point<Ty>)
2954 constexpr bool operator==(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2955 {
2956 using CommonUnit = std::common_type_t<unit, unit<ConversionFactorRhs, Ty, NsRhs>>;
2957 using CommonUnderlying = typename CommonUnit::underlying_type;
2958
2959 const auto common_lhs(CommonUnit(*this)._linearized_value);
2960 const auto common_rhs(CommonUnit(rhs)._linearized_value);
2961
2962 return abs(common_lhs - common_rhs) < std::numeric_limits<CommonUnderlying>::epsilon() * abs(common_lhs + common_rhs) ||
2963 abs(common_lhs - common_rhs) < std::numeric_limits<CommonUnderlying>::min();
2964 }
2965
2966 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2967 requires(std::integral<T> && std::integral<Ty>)
2968 constexpr bool operator==(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2969 {
2970 return value_compare(rhs) == 0;
2971 }
2976
2980 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
2981 constexpr bool operator!=(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
2982 {
2983 return !(*this == rhs);
2984 }
2985
2987
2992 constexpr underlying_type raw() const noexcept
2993 {
2994 return static_cast<underlying_type>(NumericalScale::scale(_linearized_value));
2996
3004 constexpr auto value() const noexcept
3005 {
3006 using CfTraits = traits::conversion_factor_traits<ConversionFactor>;
3007
3008 constexpr bool needs_fp = traits::is_ratio_dimensionless_cf_v<ConversionFactor> || !std::ratio_equal_v<typename CfTraits::pi_exponent_ratio, std::ratio<0>> ||
3009 !std::ratio_equal_v<typename CfTraits::translation_ratio, std::ratio<0>>;
3010
3011 using normalized_value_type = std::conditional_t<needs_fp, detail::floating_point_promotion_t<underlying_type>, underlying_type>;
3012
3014 {
3015 // Always normalize dimensionless units to base dimensionless ratio for "value()"
3016 // For ratio-dimensionless (pct/ppm/ppb), we *promote* the return type so int percent works.
3017 using Under = normalized_value_type;
3018
3019 using BaseDimlessCF = units::conversion_factor<std::ratio<1>, dimension::dimensionless>;
3020
3021 using BaseDimlessUnit = unit<BaseDimlessCF, Under, NumericalScale>;
3022
3023 return NumericalScale::scale(units::convert<BaseDimlessUnit>(*this).to_linearized());
3024 }
3025 else
3026 {
3027 return static_cast<normalized_value_type>(raw());
3028 }
3029 }
3030
3031
3035 template<ArithmeticType Ty>
3036 constexpr Ty to() const noexcept
3037 {
3038 return static_cast<Ty>(*this);
3039 }
3040
3043 * @details Converts to a different named unit of the same dimension, e.g.
3044 * `(100.0_cm).to<meters>()`. The named-template spelling of `convert()`; provided so a
3045 * single accessor reads for both underlying-type extraction (`to<double>()`) and
3046 * dimensioned conversion (`to<meters>()`).
3047 * @tparam UnitType unit class template to convert to
3048 * @returns a `UnitType<T>` containing the equivalent value to *this.
3049 */
3050 template<template<class> class UnitType>
3051 requires same_dimension<UnitType<T>, unit>
3052 constexpr UnitType<T> to() const noexcept
3053 {
3054 return UnitType<T>(*this);
3055 }
3056
3061 constexpr T to_linearized() const noexcept
3062 {
3063 return _linearized_value;
3064 }
3065
3068 * @details Converts to a different unit. Units can be converted to other units
3069 * implicitly, but this can be used in cases where the explicit notation of a conversion
3070 * is beneficial, or where an prvalue unit is needed.
3071 * @tparam Cf conversion factor of the unit to convert to
3072 * @tparam Ty underlying type of the unit to convert to
3073 * @returns a unit with the specified parameters containing the equivalent value to
3074 * *this.
3075 */
3076 template<ConversionFactorType Cf, ArithmeticType Ty = T>
3077 constexpr unit<Cf, Ty> convert() const noexcept
3078 {
3079 return unit<Cf, Ty>(*this);
3080 }
3081
3084 * @details Converts to a different unit. Units can be converted to other units
3085 * implicitly, but this can be used in cases where the explicit notation of a conversion
3086 * is beneficial, or where a prvalue unit is needed.
3087 * @tparam UnitType unit type to convert to
3088 * @returns a unit with the specified parameters containing the equivalent value to
3089 * *this.
3090 */
3091 template<template<class> class UnitType>
3092 requires same_dimension<UnitType<T>, unit>
3093 constexpr UnitType<T> convert() const noexcept
3094 {
3095 return UnitType<T>(*this);
3096 }
3097
3101
3102 template<ArithmeticType Ty>
3104 constexpr operator Ty() const noexcept
3105 {
3106 // this conversion also resolves any PI exponents, by converting from a non-zero PI ratio to a zero-pi
3107 // ratio.
3108 return static_cast<Ty>(this->value());
3109 }
3110
3112
3115 template<ArithmeticType Ty>
3117 constexpr explicit operator Ty() const noexcept
3118 {
3119 return static_cast<Ty>(this->value());
3120 }
3121
3123
3126 template<ArithmeticType Rep, RatioType Period, ConversionFactorType Cf = ConversionFactor>
3127 requires detail::is_time_conversion_factor<Cf> && detail::is_losslessly_convertible<T, Rep>
3128 constexpr operator std::chrono::duration<Rep, Period>() const noexcept
3129 {
3130 return std::chrono::duration<Rep, Period>(units::unit<units::conversion_factor<Period, dimension::time>, Rep>(*this).value());
3131 }
3132
3136 template<UnitType Unit = unit>
3137 [[nodiscard]] constexpr const char* name() const noexcept
3138 {
3139 // unit_name is specialized on the NAMED class, not this unit<...> base; resolve the named form first so a
3140 // named unit (feet) reports "feet" instead of null. A compound/unnamed unit has no registered name; report
3141 // the empty string rather than nullptr so the result is always a valid C string to print or copy.
3142 constexpr const char* n = unit_name_v<detail::rewrap_to_named_t<Unit>>;
3143 return n ? n : "";
3144 }
3145
3149 template<UnitType Unit = unit>
3150 [[nodiscard]] constexpr const char* abbreviation() const noexcept
3151 {
3152 // unit_abbreviation is specialized on the NAMED class, not this unit<...> base; resolve the named form
3153 // first so a named unit (feet) reports "ft" instead of null. A compound/unnamed unit has no registered
3154 // abbreviation; report the empty string rather than nullptr so the result is always a valid C string.
3155 constexpr const char* a = unit_abbreviation_v<detail::rewrap_to_named_t<Unit>>;
3156 return a ? a : "";
3157 }
3158
3159 template<ConversionFactorType Cf, ArithmeticType Ty, NumericalScaleType<Ty> Ns>
3160 friend class unit;
3161
3162 private:
3167 template<ConversionFactorType ConversionFactorRhs, ArithmeticType Ty, NumericalScaleType<Ty> NsRhs>
3168 constexpr auto value_compare(const unit<ConversionFactorRhs, Ty, NsRhs>& rhs) const noexcept
3169 {
3170 using CommonUnit = std::common_type_t<unit, unit<ConversionFactorRhs, Ty, NsRhs>>;
3171 if constexpr (std::is_integral_v<T> && std::is_integral_v<Ty>)
3172 {
3173 // Each side is scaled into the common unit by a whole multiplier, in the widest integer the platform has.
3174 using Wide = std::conditional_t<std::is_unsigned_v<T> && std::is_unsigned_v<Ty>,
3175 detail::widest_unsigned_int, detail::widest_signed_int>;
3176 using CommonRatio = typename traits::conversion_factor_traits<typename CommonUnit::conversion_factor>::conversion_ratio;
3177 using LhsScale = std::ratio_divide<typename traits::conversion_factor_traits<ConversionFactor>::conversion_ratio, CommonRatio>;
3178 using RhsScale = std::ratio_divide<typename traits::conversion_factor_traits<ConversionFactorRhs>::conversion_ratio, CommonRatio>;
3179
3180 if constexpr (LhsScale::den == 1 && RhsScale::den == 1 &&
3181 (std::is_signed_v<T> == std::is_signed_v<Ty> ||
3182 (sizeof(detail::widest_signed_int) > sizeof(T) && sizeof(detail::widest_signed_int) > sizeof(Ty))))
3183 {
3184 constexpr Wide lhsMultiplier = static_cast<Wide>(LhsScale::num);
3185 constexpr Wide rhsMultiplier = static_cast<Wide>(RhsScale::num);
3186 constexpr Wide cap = std::numeric_limits<Wide>::max();
3187
3188 const Wide lhsRaw = static_cast<Wide>(_linearized_value);
3189 const Wide rhsRaw = static_cast<Wide>(rhs._linearized_value);
3190
3191 // A product can exceed even the widest integer; where it would, the reconciliation below stands in.
3192 const auto fits = [](Wide value, Wide multiplier) {
3193 const Wide bound = cap / multiplier;
3194 if constexpr (std::is_unsigned_v<Wide>)
3195 return value <= bound;
3196 else
3197 return value <= bound && value >= -bound;
3198 };
3199
3200 if (fits(lhsRaw, lhsMultiplier) && fits(rhsRaw, rhsMultiplier))
3201 return lhsRaw * lhsMultiplier <=> rhsRaw * rhsMultiplier;
3202 }
3203
3204 const T lhsCommon = unit<typename CommonUnit::conversion_factor, T, NumericalScale>(*this)._linearized_value;
3205 const Ty rhsCommon = unit<typename CommonUnit::conversion_factor, Ty, NsRhs>(rhs)._linearized_value;
3206 if (std::cmp_less(lhsCommon, rhsCommon))
3207 return std::strong_ordering::less;
3208 if (std::cmp_greater(lhsCommon, rhsCommon))
3209 return std::strong_ordering::greater;
3210 return std::strong_ordering::equal;
3211 }
3212 else
3213 {
3214 const auto lhsCommon = CommonUnit(*this)._linearized_value;
3215 const auto rhsCommon = CommonUnit(rhs)._linearized_value;
3216 return lhsCommon <=> rhsCommon;
3217 }
3218 }
3219
3220 public:
3223 T _linearized_value;
3224 };
3225
3226 namespace detail
3227 {
3239
3240 // True iff T is a unit-derived class that is NOT itself the canonical unit<...> (i.e. a NAMED unit). Guarded:
3241 // unit_base_t<T> (which reads T::conversion_factor) is only well-formed for a unit, so gate on is_unit FIRST
3242 // via a helper struct — a plain arithmetic T (e.g. double) has no conversion_factor and must yield false, not
3243 // a hard error.
3244 template<class T, bool = traits::is_unit<T>::value>
3245 struct is_named_unit_impl : std::false_type
3246 {
3247 };
3248 template<class T>
3249 struct is_named_unit_impl<T, true> : std::bool_constant<!std::is_same_v<T, unit_base_t<T>>>
3250 {
3251 };
3252 template<class T>
3253 inline constexpr bool is_named_unit_v = is_named_unit_impl<T>::value;
3254
3255 // Two conversion factors are EQUIVALENT when they describe the same physical mapping — same dimension,
3256 // conversion ratio, pi exponent, and datum — even if they are different C++ types (a flattened
3257 // `conversion_factor<ratio<1,100>, length>` versus the composed `centi<meters_>` that `centimeters` is
3258 // registered as). Type identity is stricter than equivalence; a reconciliation result that is equivalent
3259 // to an operand's unit should still recover that operand's friendly name.
3260 template<class Cf1, class Cf2>
3261 inline constexpr bool is_equivalent_conversion_factor_v =
3262 traits::is_same_dimension_conversion_factor_v<Cf1, Cf2> &&
3263 std::ratio_equal_v<typename Cf1::conversion_ratio, typename Cf2::conversion_ratio> &&
3264 std::ratio_equal_v<typename Cf1::pi_exponent_ratio, typename Cf2::pi_exponent_ratio> &&
3265 std::ratio_equal_v<typename Cf1::translation_ratio, typename Cf2::translation_ratio>;
3266
3267 // A conversion factor is RAW when it carries no registered name of its own — a bare reconciliation result
3268 // such as the flattened gcd of meters and centimeters, for which `named_class_of` finds no registration and
3269 // `rewrap_to_named` is the identity. A named unit's registered factor (meters_, joules_, …) is NOT raw: it
3270 // resolves to its named class. Equivalence-based name recovery fires only for a RAW factor, because
3271 // recovering a name for an already-named factor could rename one physical kind to another that shares its
3272 // dimension and ratio (torque's newton_meters_ and energy's joules_ are equivalent) — so recovery is
3273 // restricted to the anonymous reconciliation results that have no name to preserve.
3274 template<class Cf>
3275 inline constexpr bool is_raw_conversion_factor_v =
3276 std::is_void_v<decltype(named_class_of(static_cast<Cf*>(nullptr), static_cast<linear_scale*>(nullptr)))>;
3277
3278 // Re-wrap a computed base result `unit<Cf, U, Ns>` into a NAMED unit when a candidate operand `Named` is a
3279 // named unit of the SAME conversion_factor: the friendly name is preserved through the trait (so
3280 // common_type<meters<int>, meters<double>> is meters<double>, not unit<meters_, double, linear_scale>). When no
3281 // candidate matches (mixed names, or a plain-unit operand), the base result stands. `Base` is the plain unit<>.
3282 template<class Base, class Named, class = void>
3283 struct rewrap_named
3284 {
3285 using type = Base;
3286 };
3287 template<class Base, class Named>
3288 struct rewrap_named<Base, Named,
3289 std::enable_if_t<is_named_unit_v<Named> && std::is_same_v<typename Base::conversion_factor, typename Named::conversion_factor>>>
3290 {
3291 using type = typename Named::template rebind<typename Base::underlying_type>;
3292 };
3293 // Equivalence recovery: when `Base`'s factor is RAW (an anonymous reconciliation result, e.g. the flattened
3294 // gcd of meters and centimeters) and is equivalent to a named operand's factor, recover that operand's name.
3295 // This names an m − cm result `centimeters` and an hr − min result `minutes` where exact-type matching missed
3296 // them, without renaming an already-named result (the raw guard excludes strong factors such as joules_).
3297 template<class Base, class Named>
3298 struct rewrap_named<Base, Named,
3299 std::enable_if_t<is_named_unit_v<Named> && !std::is_same_v<typename Base::conversion_factor, typename Named::conversion_factor> &&
3300 is_raw_conversion_factor_v<typename Base::conversion_factor> &&
3301 is_equivalent_conversion_factor_v<typename Base::conversion_factor, typename Named::conversion_factor>>>
3302 {
3303 using type = typename Named::template rebind<typename Base::underlying_type>;
3304 };
3305 template<class Base, class Named>
3306 using rewrap_named_t = typename rewrap_named<Base, Named>::type;
3307
3308 // Identity fallback for the CF-struct -> named-class ADL map (the exact registrations are emitted per named
3309 // unit by UNIT_REGISTER_NAMED_CLASS). Worst match (trailing ellipsis); returns void to signal "no named class
3310 // for this CF". decltype-only, never defined. A real registration's exact strong-CF* parameter beats this.
3311 template<class ConversionFactor, class Scale>
3312 void named_class_of(ConversionFactor*, Scale*, ...);
3313
3314 // Map a plain unit<Cf, U, Ns> to its NAMED class when one is registered for Cf, else identity. Used by the
3315 // arithmetic operators so a computed result (e.g. unit<square_meters_, int, linear_scale>) is REPORTED as the
3316 // friendly named type (square_meters<int>). Rebinds the registered class to U so the underlying flows through.
3317 // SFINAE-guarded: only a unit whose Cf has a registration is rewrapped; everything else is identity.
3318 // (The primary template + the rewrap_to_named_t alias are forward-declared before the unit class so unit's
3319 // name()/abbreviation() members can name them; here we DEFINE the primary and the specialization.)
3320 template<class U, class>
3321 struct rewrap_to_named
3322 {
3323 using type = U;
3324 };
3325 template<class U>
3326 struct rewrap_to_named<U,
3327 std::enable_if_t<traits::is_unit<U>::value &&
3328 !std::is_void_v<decltype(named_class_of(static_cast<typename U::conversion_factor*>(nullptr),
3329 static_cast<typename U::numerical_scale_type*>(nullptr)))>>>
3330 {
3331 using type = typename decltype(named_class_of(static_cast<typename U::conversion_factor*>(nullptr),
3332 static_cast<typename U::numerical_scale_type*>(nullptr)))::template rebind<typename U::underlying_type>;
3333 };
3334 } // namespace detail
3335
3336 namespace traits
3337 {
3338 // A NAMED unit (a class deriving from unit<...>) unwraps to its base for these exact-pattern traits, so
3339 // replace_underlying / floating_point_promotion behave for named units exactly as for the plain unit<...>.
3340 // The plain-unit<...> specializations are declared earlier; these constrained ones fire only for a named unit.
3341 template<class Unit, class Underlying>
3342 requires ::units::detail::is_named_unit_v<Unit>
3343 struct replace_underlying<Unit, Underlying>
3344 {
3345 // PRESERVE the named type: rebind it to the new underlying (meters<int> -> meters<double>), rather than
3346 // decaying to the plain unit<...> base. Keeps trait results as friendly as the inputs.
3347 using type = typename Unit::template rebind<Underlying>;
3348 };
3349 } // namespace traits
3350
3351 namespace detail
3352 {
3353 template<class Unit>
3354 requires is_named_unit_v<Unit>
3355 struct floating_point_promotion<Unit>
3356 {
3357 // Promote the UNDERLYING type but PRESERVE the friendly named type: rebind the named unit to the promoted
3358 // underlying (meters<int> -> meters<double>), so ceil/floor/round/hypot report the named result, not unit<>.
3359 using type = typename Unit::template rebind<typename floating_point_promotion<unit_base_t<Unit>>::type::underlying_type>;
3360 };
3361 } // namespace detail
3362
3363 //------------------------------
3364 // UNIT NON-MEMBER FUNCTIONS
3365 //------------------------------
3366
3370 * @details make_unit can be used to construct a unit container from an arithmetic type, as an alternative to
3371 * using the explicit constructor. Unlike the explicit constructor it forces the user to explicitly
3372 * specify the units.
3373 * @tparam UnitType Type to construct.
3374 * @tparam T Arithmetic type.
3375 * @param[in] value Arithmetic value that represents a quantity in units of `UnitType`.
3376 */
3377 template<UnitType UnitType, ArithmeticType T>
3378 requires detail::is_losslessly_convertible<T, typename UnitType::underlying_type>
3379 constexpr UnitType make_unit(const T value) noexcept
3380 {
3381 return UnitType(value);
3382 }
3383
3384 //-----------------------------------------
3385 // UNIT-LABEL STRING BUILDERS
3386 //-----------------------------------------
3387
3388#if defined(UNIT_LIB_ENABLE_STRING)
3389
3390 namespace detail
3391 {
3392 //----------------------------------------------------------------------------------------------------------------------
3393 // FUNCTION: dimension_to_string [static]
3394 //----------------------------------------------------------------------------------------------------------------------
3401 //----------------------------------------------------------------------------------------------------------------------
3402 template<class D, class E>
3403 std::string dimension_to_string(const dim<D, E>&)
3404 {
3405 std::string s;
3406 if constexpr (E::num != 0)
3407 {
3408 s.append(" ").append(D::abbreviation);
3409 }
3410 if constexpr (E::num != 0 && E::num != 1)
3411 {
3412 s.append("^").append(std::to_string(E::num));
3413 }
3414 if constexpr (E::den != 1)
3415 {
3416 s.append("/").append(std::to_string(E::den));
3417 }
3418 return s;
3419 }
3421 //----------------------------------------------------------------------------------------------------------------------
3422 // FUNCTION: dimension_to_string [static]
3423 //----------------------------------------------------------------------------------------------------------------------
3426
3427 //----------------------------------------------------------------------------------------------------------------------
3428 template<class... Dims>
3429 std::string dimension_to_string(const dimension_t<Dims...>&)
3430 {
3431 std::string s;
3432 ((s.append(dimension_to_string(Dims{}))), ...);
3433 return s;
3434 }
3435
3436 //----------------------------------------------------------------------------------------------------------------------
3437 // FUNCTION: unit_label [static]
3438 //----------------------------------------------------------------------------------------------------------------------
3450 //----------------------------------------------------------------------------------------------------------------------
3456
3459 enum class label_form
3460 {
3461 abbreviation,
3462 name,
3463 base
3464 };
3465
3466 template<label_form Form = label_form::abbreviation, ConversionFactorType ConversionFactor, ArithmeticType T, NumericalScaleType<T> NumericalScale>
3467 std::string unit_label(const unit<ConversionFactor, T, NumericalScale>&)
3468 {
3469 // The name/abbreviation traits are specialized on the NAMED class, not the plain unit<...> base,
3470 // so resolve the named form first and query THAT (a named unit prints its name/abbreviation).
3471 using NamedForm = detail::rewrap_to_named_t<unit<ConversionFactor, T, NumericalScale>>;
3473
3474 if constexpr (Form == label_form::base)
3475 {
3476 // SI base-dimension list, regardless of the unit's own name (the caller base-converts the value).
3477 if constexpr (!DimType::empty)
3478 return dimension_to_string(DimType{});
3479 else
3480 return std::string{};
3481 }
3482 else if constexpr (Form == label_form::name && unit_name_v<NamedForm>)
3483 {
3484 return std::string(" ").append(unit_name<NamedForm>::value);
3485 }
3486 else if constexpr (unit_abbreviation_v<NamedForm>)
3487 {
3488 return std::string(" ").append(unit_abbreviation<NamedForm>::value);
3489 }
3490 else
3491 {
3492 // Unnamed unit: its honest label IS the base-dimension list (no own symbol exists).
3493 if constexpr (!DimType::empty)
3494 return dimension_to_string(DimType{});
3495 else
3496 return std::string{};
3497 }
3498 }
3499
3500 //----------------------------------------------------------------------------------------------------------------------
3501 // FUNCTION: label_uses_base_unit [static]
3502 //----------------------------------------------------------------------------------------------------------------------
3504 /// @details An unnamed unit is rendered in its BASE unit (its value must be converted to the base
3505 /// before the dimension label applies); a named unit prints its value as-is. This
3506 /// predicate lets the value-rendering paths decide whether to convert to the base unit.
3507 /// @tparam ConversionFactor the unit's conversion factor.
3508 /// @tparam T the unit's underlying arithmetic type.
3509 /// @tparam NumericalScale the unit's numerical scale.
3510 /// @return `true` when the unit is unnamed (dimension-labelled), `false` when it is named.
3511 //----------------------------------------------------------------------------------------------------------------------
3512 template<ConversionFactorType ConversionFactor, ArithmeticType T, NumericalScaleType<T> NumericalScale>
3513 inline constexpr bool label_uses_base_unit()
3514 {
3515 using NamedForm = detail::rewrap_to_named_t<unit<ConversionFactor, T, NumericalScale>>;
3516 return !static_cast<bool>(unit_abbreviation_v<NamedForm>);
3517 }
3518 } // namespace detail
3519
3520#endif // UNIT_LIB_ENABLE_STRING
3521
3522#if defined(UNIT_LIB_ENABLE_FORMAT)
3523
3524 //-----------------------------------------
3525 // std::format SUPPORT
3526 //-----------------------------------------
3527
3528 namespace detail
3529 {
3530 //----------------------------------------------------------------------------------------------------------------------
3531 // STRUCT: unit_format_options
3532 //----------------------------------------------------------------------------------------------------------------------
3534 //----------------------------------------------------------------------------------------------------------------------
3535 struct unit_format_options
3536 {
3537 label_form form = label_form::abbreviation;
3538 bool showValue = true;
3539 bool showUnit = true;
3540 bool customSep = false;
3541 std::string separator = " ";
3542 };
3543 } // namespace detail
3544
3545#endif // UNIT_LIB_ENABLE_FORMAT
3546
3547#if !defined(UNIT_LIB_DISABLE_IOSTREAM)
3548
3549 //-----------------------------------------
3550 // OSTREAM OPERATOR FOR EPHEMERAL UNITS
3551 //-----------------------------------------
3552
3553 template<class D, class E>
3554 std::ostream& operator<<(std::ostream& os, const dim<D, E>&)
3555 {
3556 if constexpr (E::num != 0)
3557 os << ' ' << D::abbreviation;
3558 if constexpr (E::num != 0 && E::num != 1)
3559 {
3560 os << "^" << E::num;
3561 }
3562 if constexpr (E::den != 1)
3563 {
3564 os << "/" << E::den;
3565 }
3566 return os;
3567 }
3568
3569 template<class... Dims>
3570 std::ostream& operator<<(std::ostream& os, const dimension_t<Dims...>&)
3571 {
3572 ((os << Dims{}), ...);
3573 return os;
3574 }
3575
3576 template<ConversionFactorType ConversionFactor, ArithmeticType T, NumericalScaleType<T> NumericalScale>
3577 std::ostream& operator<<(std::ostream& os, const unit<ConversionFactor, T, NumericalScale>& obj)
3578 {
3579 using BaseConversion = conversion_factor<std::ratio<1>, typename ConversionFactor::dimension_type>;
3581 using PromotedBaseUnit = unit<BaseConversion, detail::floating_point_promotion_t<T>, NumericalScale>;
3582
3583 // The abbreviation trait is specialized on the NAMED class, not the plain unit<...> base this overload
3584 // deduces; resolve the named form first and query THAT so a named unit (meters_per_second -> "mps") prints
3585 // its abbreviation instead of the dimension form.
3586 using NamedForm = detail::rewrap_to_named_t<unit<ConversionFactor, T, NumericalScale>>;
3587
3588 if constexpr (unit_abbreviation_v<NamedForm>)
3589 {
3590 os << obj.raw();
3591 }
3592 else
3593 {
3594 os << std::conditional_t<detail::is_losslessly_convertible_unit<std::decay_t<decltype(obj)>, BaseUnit>, BaseUnit, PromotedBaseUnit>(obj).raw();
3595 }
3596 os << detail::unit_label(obj);
3597
3598 return os;
3599 }
3600
3601 //----------------------------
3602 // to_string
3603 //----------------------------
3604
3605 template<ConversionFactorType ConversionFactor, ArithmeticType T, NumericalScaleType<T> NumericalScale>
3606 std::string to_string(const unit<ConversionFactor, T, NumericalScale>& obj)
3607 {
3608 using BaseConversion = conversion_factor<std::ratio<1>, typename ConversionFactor::dimension_type>;
3610 using PromotedBaseUnit = unit<BaseConversion, detail::floating_point_promotion_t<T>, NumericalScale>;
3611
3612 // The abbreviation trait (unit_name/unit_abbreviation) is specialized on the NAMED class, not the plain
3613 // unit<...> base this overload deduces, so resolve the named form first and query THAT — a named unit
3614 // (feet<double>) then still prints its abbreviation ("ft") instead of falling to the dimension path.
3615 using NamedForm = detail::rewrap_to_named_t<unit<ConversionFactor, T, NumericalScale>>;
3616
3617 std::string s;
3618 if constexpr (unit_abbreviation_v<NamedForm>)
3619 s = detail::to_string(obj.raw());
3620 else
3621 s = detail::to_string(std::conditional_t<detail::is_losslessly_convertible_unit<std::decay_t<decltype(obj)>, BaseUnit>, BaseUnit, PromotedBaseUnit>(obj).raw());
3622
3623 s.append(detail::unit_label(obj));
3624 return s;
3625 }
3626#endif
3627
3628 //------------------------------
3629 // std::ratio helpers
3630 //------------------------------
3631 // DOXYGEN IGNORE
3633 namespace detail
3634 {
3638 template<RatioType Ratio1, RatioType Ratio2>
3639 using ratio_gcd = std::ratio<std::gcd(Ratio1::num, Ratio2::num), std::lcm(Ratio1::den, Ratio2::den)>;
3640
3647 template<RatioType Ratio1, RatioType Ratio2>
3648 using common_baggage_ratio = std::conditional_t<std::ratio_equal_v<Ratio1, Ratio2>, Ratio1, std::ratio<0>>;
3649 } // namespace detail // END DOXYGEN IGNORE
3651} // end namespace units
3652
3653//------------------------------
3654// std::common_type
3655//------------------------------
3656
3657namespace std
3658{
3659 /**
3660 * @ingroup STDTypeTraits
3661 * @brief common type of units
3662 * @details The `type` alias of the `std::common_type` of two `unit`s of the same dimension is the least precise
3663 * `unit` to which both `unit` arguments can be converted to without requiring a division operation or
3664 * truncating any value of these conversions, although floating-point units may have round-off errors.
3665 * If the units have mixed scales, preference is given to `linear_scale` for their common type.
3666 */
3667 template<class ConversionFactorLhs, class Tx, class ConversionFactorRhs, class Ty, class NumericalScale>
3668 struct common_type<units::unit<ConversionFactorLhs, Tx, NumericalScale>, units::unit<ConversionFactorRhs, Ty, NumericalScale>>
3669 : std::enable_if<units::traits::is_same_dimension_conversion_factor_v<ConversionFactorLhs, ConversionFactorRhs>,
3670 units::unit<
3671 units::traits::strong_t<units::conversion_factor<units::detail::ratio_gcd<typename ConversionFactorLhs::conversion_ratio, typename ConversionFactorRhs::conversion_ratio>,
3672 units::traits::dimension_of_t<ConversionFactorLhs>, units::detail::ratio_gcd<typename ConversionFactorLhs::pi_exponent_ratio, typename ConversionFactorRhs::pi_exponent_ratio>,
3673 units::detail::common_baggage_ratio<typename ConversionFactorLhs::translation_ratio, typename ConversionFactorRhs::translation_ratio>>>,
3674 common_type_t<Tx, Ty>, NumericalScale>>
3675 {
3676 };
3677
3678 // In the case the two units are the same type, just use that type as common type
3679 template<class UnitConversionT, class T, class NonLinearScale>
3680 struct common_type<units::unit<UnitConversionT, T, NonLinearScale>, units::unit<UnitConversionT, T, NonLinearScale>>
3681 {
3683 };
3684
3685 // A NAMED unit is a class deriving from unit<...>; the exact-pattern specializations above do not match it. When
3686 // either operand is a named unit, compute the common type of the canonical unit<...> BASES, then RE-WRAP the result
3687 // into the named type when an operand shares its conversion_factor — so common_type<meters<int>, meters<double>> is
3688 // meters<double>, not the plain unit<...> (the friendly name survives through the trait). Constrained to "both are
3689 // units AND at least one is named" so it never overlaps the exact-unit<...> cases above.
3690 template<class Lhs, class Rhs>
3692 (units::detail::is_named_unit_v<Lhs> || units::detail::is_named_unit_v<Rhs>) &&
3693 // ONLY when the plain-base common type EXISTS (same dimension). For different dimensions the bases have
3694 // no common type, so this specialization must be SFINAE-EMPTY too (no `type`) — matching the plain
3695 // unit<...> behavior. Without this, computing `base` below is a hard error on stricter compilers
3696 // (clang) where g++ tolerated the absent member.
3697 requires { typename common_type<units::detail::unit_base_t<Lhs>, units::detail::unit_base_t<Rhs>>::type; })
3698 struct common_type<Lhs, Rhs>
3699 {
3700 private:
3701 using base = common_type_t<units::detail::unit_base_t<Lhs>, units::detail::unit_base_t<Rhs>>;
3702 // prefer to re-wrap into Lhs's name; if that doesn't share the CF, try Rhs's.
3703 using viaLhs = units::detail::rewrap_named_t<base, Lhs>;
3704
3705 public:
3706 using type = units::detail::rewrap_named_t<viaLhs, Rhs>;
3707 };
3708
3709 // A NAMED DIMENSIONLESS unit (e.g. percent) mixed with a plain arithmetic scalar: the exact-unit<...>-vs-scalar
3710 // specializations below do not match the named class, so unwrap the named operand to its base and re-wrap the
3711 // result to keep the friendly name. dimensionless units stay fully interchangeable with int/double. Gated on
3712 // is_dimensionless_unit (mirroring the plain unit<...>-vs-scalar specializations): a DIMENSIONED named unit + a
3713 // scalar must NOT match — it falls through to the primary std::common_type and is SFINAE-empty (no `type`), the
3714 // same SFINAE-friendly behavior the plain form has (never a hard error).
3715 template<class Named, class Scalar>
3716 requires(units::detail::is_named_unit_v<Named> && std::is_arithmetic_v<Scalar> &&
3718 struct common_type<Named, Scalar>
3719 {
3720 using type = units::detail::rewrap_named_t<common_type_t<units::detail::unit_base_t<Named>, Scalar>, Named>;
3721 };
3722 template<class Scalar, class Named>
3723 requires(units::detail::is_named_unit_v<Named> && std::is_arithmetic_v<Scalar> &&
3725 struct common_type<Scalar, Named>
3726 {
3727 using type = units::detail::rewrap_named_t<common_type_t<Scalar, units::detail::unit_base_t<Named>>, Named>;
3729
3730 template<class Ratio, class T, class NumericalScale, class Rep, class Period>
3731 struct common_type<units::unit<units::detail::time_conversion_factor<Ratio>, T, NumericalScale>, chrono::duration<Rep, Period>>
3732 : std::common_type<units::unit<units::detail::time_conversion_factor<Ratio>, T, NumericalScale>, decltype(units::unit{chrono::duration<Rep, Period>{}})>
3733 {
3734 };
3736 template<class ConversionFactor, class T, class NumericalScale, class Rep, class Period>
3737 struct common_type<chrono::duration<Rep, Period>, units::unit<ConversionFactor, T, NumericalScale>>
3738 : std::common_type<units::unit<ConversionFactor, T, NumericalScale>, chrono::duration<Rep, Period>>
3739 {
3740 };
3741
3742 template<class ConversionFactor, class Tx, class NumericalScale, class Ty>
3743 requires std::is_arithmetic_v<Ty> // constrain so a unit `Ty` never matches (that is a unit+unit case above)
3744 struct common_type<Ty, units::unit<ConversionFactor, Tx, NumericalScale>>
3745 : std::enable_if<units::traits::is_dimensionless_unit<units::unit<ConversionFactor, Tx, NumericalScale>>::value,
3746 units::unit<units::conversion_factor<std::ratio<1>, units::dimension::dimensionless>, common_type_t<Tx, Ty>, NumericalScale>>
3747 {
3748 };
3749
3750 template<class ConversionFactor, class Tx, class NumericalScale, class Ty>
3751 requires std::is_arithmetic_v<Ty> // constrain so a unit `Ty` never matches (that is a unit+unit case above)
3752 struct common_type<units::unit<ConversionFactor, Tx, NumericalScale>, Ty>
3753 : std::enable_if<units::traits::is_dimensionless_unit<units::unit<ConversionFactor, Tx, NumericalScale>>::value,
3754 units::unit<units::conversion_factor<std::ratio<1>, units::dimension::dimensionless>, common_type_t<Tx, Ty>, NumericalScale>>
3755 {
3756 };
3757 // DOXYGEN IGNORE
3762 template<class ConversionFactorLhs, class Tx, class ConversionFactorRhs, class Ty>
3763 struct common_type<units::unit<ConversionFactorLhs, Tx, units::linear_scale>, units::unit<ConversionFactorRhs, Ty, units::decibel_scale>>
3764 : common_type<units::unit<ConversionFactorLhs, Tx, units::linear_scale>, units::unit<ConversionFactorRhs, Ty, units::linear_scale>>
3765 {
3766 };
3767
3768 template<class ConversionFactorLhs, class Tx, class ConversionFactorRhs, class Ty>
3769 struct common_type<units::unit<ConversionFactorLhs, Tx, units::decibel_scale>, units::unit<ConversionFactorRhs, Ty, units::linear_scale>>
3770 : common_type<units::unit<ConversionFactorLhs, Tx, units::linear_scale>, units::unit<ConversionFactorRhs, Ty, units::linear_scale>>
3771 {
3772 };
3773 // END DOXYGEN IGNORE
3775} // namespace std
3776
3777namespace units
3778{
3779 //------------------------------
3780 // UNIT_CAST
3781 //------------------------------
3782
3789 * @code meter_t unitVal(5);
3790 * double value = units::unit_cast<double>(unitVal); // value == 5.0
3791 * @endcode
3792 * @tparam T Type to cast the unit type to. Shall be an arithmetic type.
3793 * @tparam Unit Type of the unit to cast to.
3794 * @param value Unit value to cast.
3795 * @sa unit::to
3796 */
3797 template<ArithmeticType T, UnitType Unit>
3798 constexpr T unit_cast(const Unit& value) noexcept
3799 {
3800 return static_cast<T>(value);
3801 }
3802
3803 //------------------------------
3804 // NUMERICAL SCALE TRAITS
3805 //------------------------------
3806
3807 // forward declaration
3808 namespace traits
3812
3817 template<typename... T>
3818 struct has_linear_scale : std::conjunction<std::is_base_of<linear_scale, T>...>
3819 {
3820 };
3821
3822 template<typename... T>
3823 inline constexpr bool has_linear_scale_v = has_linear_scale<T...>::value;
3827
3832 template<typename... T>
3833 struct has_decibel_scale : std::conjunction<std::is_base_of<decibel_scale, T>...>
3834 {
3835 };
3836
3837 template<typename... T>
3838 inline constexpr bool has_decibel_scale_v = has_decibel_scale<T...>::value;
3839 } // namespace traits
3840
3841 //----------------------------------
3842 // NUMERICAL SCALES
3843 //----------------------------------
3844
3845 // Non-linear transforms may be used to pre- and post-scale units which are defined in terms of non-
3846 // linear functions of their current value. A good example of a non-linear scale would be a
3847 // logarithmic or decibel scale
3848
3849 //------------------------------
3850 // LINEAR SCALE
3851 //------------------------------
3852
3860 {
3863
3867 template<class T>
3868 static constexpr T linearize(const T value) noexcept
3869 {
3870 return value;
3872
3875
3876
3879 template<class T>
3880 static constexpr T scale(const T value) noexcept
3881 {
3882 return value;
3883 }
3884 };
3885
3886 //----------------------------------
3887 // dimensionless (LINEAR) UNITS
3888 //----------------------------------
3889
3890 // dimensionless units are the *ONLY* units implicitly convertible to/from built-in types.
3891
3892 using dimensionless_ = conversion_factor<std::ratio<1>, dimension::dimensionless>;
3893
3894 namespace detail
3895 {
3896 // ADL registration of the dimensionless strong type (see detail::strong_name, #357). The base-form CF maps
3897 // back to the canonical dimensionless conversion_factor. Declared, never defined (used only in decltype).
3898 conversion_factor<std::ratio<1>, dimension::dimensionless> strong_name(
3899 units::detail::conversion_factor_base_t<dimensionless_>*);
3901 // The PURE dimensionless unit (ratio 1) stays a plain alias to unit<...>, NOT a named class: it must remain
3902 // totally interchangeable with the built-in arithmetic types (int/double) and identity-equal to its unit<...>
3903 // base (so common_type<dimensionless<int>, int> is the plain unit and dimensionless<int> IS unit<dimensionless_,
3904 // int>). A distinct class would break that interchangeability. Named ratio-dimensionless units (percent/ppm/...)
3905 // are still classes — they carry a meaningful name.
3906 template<class Underlying = UNIT_LIB_DEFAULT_TYPE>
3907 using dimensionless = unit<traits::strong_t<conversion_factor<std::ratio<1>, dimension::dimensionless>>, Underlying, linear_scale>;
3908
3910
3911 //----------------------------------------
3912 // UNIT COMPOUND ASSIGNMENT OPERATORS
3913 //----------------------------------------
3914
3915 // DOXYGEN IGNORE
3916 namespace detail
3917 {
3921 template<class T>
3922 struct type_identity
3923 {
3924 using type = T;
3925 };
3926
3927 template<class T>
3928 using type_identity_t = typename type_identity<T>::type;
3929 } // namespace detail // END DOXYGEN IGNORE
3931
3932 template<UnitType UnitTypeLhs>
3934 constexpr UnitTypeLhs& operator+=(UnitTypeLhs& lhs, const detail::type_identity_t<UnitTypeLhs>& rhs) noexcept
3935 {
3936 lhs = lhs + rhs;
3937 return lhs;
3939
3943
3945 template<UnitType UnitTypeLhs>
3947 constexpr UnitTypeLhs& operator+=(UnitTypeLhs& lhs, const detail::type_identity_t<UnitTypeLhs>& rhs) noexcept
3948 {
3949 lhs = UnitTypeLhs(lhs.raw() + rhs.raw());
3950 return lhs;
3951 }
3952
3953 template<UnitType UnitTypeLhs, ArithmeticType T>
3955 constexpr UnitTypeLhs& operator+=(UnitTypeLhs& lhs, T rhs) noexcept
3956 {
3957 lhs = lhs + rhs;
3958 return lhs;
3959 }
3960
3961 template<RatioDimensionlessUnitType U, ArithmeticType T>
3962 requires(traits::has_linear_scale_v<U>)
3963 constexpr U& operator+=(U& lhs, T rhs) noexcept
3964 {
3965 using Underlying = typename U::underlying_type;
3966 using R = typename U::conversion_factor::conversion_ratio;
3967
3968 // points_per_one converts "fraction-space 1.0" into "points" for this unit.
3969 // Example: percent ratio = 1/100 -> points_per_one = 100
3970 constexpr long double points_per_one = static_cast<long double>(R::den) / static_cast<long double>(R::num);
3971
3972 // Do the math in points space to avoid truncation of lhs.value() for integral percent.
3973 const long double new_points = static_cast<long double>(lhs.raw()) + (static_cast<long double>(rhs) * points_per_one);
3974
3975 if constexpr (std::is_integral_v<Underlying>)
3976 {
3977 lhs = U(static_cast<Underlying>(std::llround(new_points)));
3978 }
3979 else
3980 {
3981 lhs = U(static_cast<Underlying>(new_points));
3982 }
3983
3984 return lhs;
3985 }
3986
3987 template<RatioDimensionlessUnitType U, DimensionlessUnitType D>
3988 requires(traits::has_linear_scale_v<U, D> && !RatioDimensionlessUnitType<D>)
3989 constexpr U& operator+=(U& lhs, const D& rhs) noexcept
3990 {
3991 // rhs.value() is plain scalar (e.g. dimensionless<int>(1) => 1)
3992 return (lhs += rhs.value());
3993 }
3994
3995 template<RatioDimensionlessUnitType U, ArithmeticType T>
3996 requires(traits::has_linear_scale_v<U>)
3997 constexpr U& operator-=(U& lhs, T rhs) noexcept
3998 {
3999 using Underlying = typename U::underlying_type;
4000 using R = typename U::conversion_factor::conversion_ratio;
4001
4002 constexpr long double points_per_one = static_cast<long double>(R::den) / static_cast<long double>(R::num);
4003
4004 const long double new_points = static_cast<long double>(lhs.raw()) - (static_cast<long double>(rhs) * points_per_one);
4005
4006 if constexpr (std::is_integral_v<Underlying>)
4007 {
4008 lhs = U(static_cast<Underlying>(std::llround(new_points)));
4009 }
4010 else
4011 {
4012 lhs = U(static_cast<Underlying>(new_points));
4013 }
4014
4015 return lhs;
4016 }
4017
4018 template<UnitType UnitTypeLhs>
4020 constexpr UnitTypeLhs& operator-=(UnitTypeLhs& lhs, const detail::type_identity_t<UnitTypeLhs>& rhs) noexcept
4021 {
4022 lhs = lhs - rhs;
4023 return lhs;
4025
4029
4031 template<UnitType UnitTypeLhs>
4033 constexpr UnitTypeLhs& operator-=(UnitTypeLhs& lhs, const detail::type_identity_t<UnitTypeLhs>& rhs) noexcept
4034 {
4035 lhs = UnitTypeLhs(lhs.raw() - rhs.raw());
4036 return lhs;
4037 }
4038
4039 template<UnitType UnitTypeLhs, ArithmeticType T>
4041 constexpr UnitTypeLhs& operator-=(UnitTypeLhs& lhs, const T& rhs) noexcept
4042 {
4043 lhs = lhs - rhs;
4044 return lhs;
4045 }
4046
4047 template<RatioDimensionlessUnitType U, DimensionlessUnitType D>
4048 requires(traits::has_linear_scale_v<U, D> && !RatioDimensionlessUnitType<D>)
4049 constexpr U& operator-=(U& lhs, const D& rhs) noexcept
4050 {
4051 return (lhs -= rhs.value());
4052 }
4053
4054 template<UnitType UnitTypeLhs, ArithmeticType T>
4056 constexpr UnitTypeLhs& operator*=(UnitTypeLhs& lhs, const T& rhs)
4057 {
4058 // The rhs is taken as its own arithmetic type (not narrowed to the lhs underlying type at the call
4059 // boundary), so a value-narrowing scale (e.g. meters<int> *= 2.0) applies normal conversion rules. The
4060 // narrowing is performed by an implicit conversion into a local of the lhs's underlying type, which surfaces
4061 // the compiler's -Wfloat-conversion warning naming `meters<int>::underlying_type (aka int)` rather than
4062 // truncating silently; it is a warning, not an error, and the result stays a UnitTypeLhs.
4063 typename UnitTypeLhs::underlying_type scaled = lhs.raw() * rhs;
4064 lhs = UnitTypeLhs(scaled, linearized_value);
4065 return lhs;
4066 }
4067
4068 template<RatioDimensionlessUnitType U, RatioDimensionlessUnitType URhs>
4069 requires(traits::has_linear_scale_v<U, URhs>)
4070 constexpr U& operator*=(U& lhs, const URhs& rhs) noexcept
4071 {
4072 using LhsUnder = typename U::underlying_type;
4073 using RhsUnder = typename URhs::underlying_type;
4074
4075 using Calc0 = std::common_type_t<LhsUnder, RhsUnder>;
4076 using Calc = detail::floating_point_promotion_t<Calc0>;
4077
4078 // rhs interpreted as normalized fraction (e.g. 200_pct -> 2.0, 2_pct -> 0.02)
4079 const Calc rhs_frac = static_cast<Calc>(rhs.value());
4080
4081 // lhs.raw() is "points" (e.g. 12_pct raw() == 12)
4082 const Calc new_points = static_cast<Calc>(lhs.raw()) * rhs_frac;
4083
4084 if constexpr (std::is_integral_v<LhsUnder>)
4085 {
4086 // Deterministic: truncate toward zero
4087 lhs = U(static_cast<LhsUnder>(new_points));
4088 }
4089 else
4090 {
4091 lhs = U(static_cast<LhsUnder>(new_points));
4092 }
4093
4094 return lhs;
4095 }
4096
4097 template<RatioDimensionlessUnitType U>
4098 requires(units::traits::has_linear_scale_v<U>)
4099 constexpr U& operator*=(U& lhs, const U& rhs) noexcept
4100 {
4101 using Underlying = typename U::underlying_type;
4102 using R = typename U::conversion_factor::conversion_ratio;
4103
4104 // percent: 1/100 -> points_per_one = 100
4105 constexpr long double points_per_one = static_cast<long double>(R::den) / static_cast<long double>(R::num);
4106
4107 const long double lhs_frac = static_cast<long double>(lhs.value()); // normalized fraction
4108 const long double rhs_frac = static_cast<long double>(rhs.value()); // normalized fraction
4109
4110 const long double out_frac = lhs_frac * rhs_frac;
4111 const long double out_points = out_frac * points_per_one;
4112
4113 if constexpr (std::is_integral_v<Underlying>)
4114 {
4115 lhs = U(static_cast<Underlying>(std::llround(out_points)));
4116 }
4117 else
4118 {
4119 lhs = U(static_cast<Underlying>(out_points));
4120 }
4121
4122 return lhs;
4123 }
4124
4125 template<RatioDimensionlessUnitType U, units::ArithmeticType T>
4126 requires(units::traits::has_linear_scale_v<U>)
4127 constexpr U& operator*=(U& lhs, T rhs) noexcept
4128 {
4129 // scalar is interpreted as base-dimensionless fraction (world-2)
4130 // so rhs = 2 means multiply fraction by 2
4131 using Underlying = typename U::underlying_type;
4132 using R = typename U::conversion_factor::conversion_ratio;
4133
4134 constexpr long double points_per_one = static_cast<long double>(R::den) / static_cast<long double>(R::num);
4135
4136 const long double lhs_frac = static_cast<long double>(lhs.value());
4137 const long double out_frac = lhs_frac * static_cast<long double>(rhs);
4138 const long double out_pts = out_frac * points_per_one;
4139
4140 if constexpr (std::is_integral_v<Underlying>)
4141 {
4142 lhs = U(static_cast<Underlying>(std::llround(out_pts)));
4143 }
4144 else
4145 {
4146 lhs = U(static_cast<Underlying>(out_pts));
4147 }
4148 return lhs;
4149 }
4150
4151 template<RatioDimensionlessUnitType U, DimensionlessUnitType D>
4152 requires(units::traits::has_linear_scale_v<U, D> && !RatioDimensionlessUnitType<D>)
4153 constexpr U& operator*=(U& lhs, const D& rhs) noexcept
4154 {
4155 // dimensionless is a scalar fraction; use its numeric value
4156 return (lhs *= rhs.value());
4157 }
4158
4159 // scale a dimensioned quantity by a dimensionless quantity: use its numeric value and route through the
4160 // arithmetic overload above (preserves the warn-on-lossy-integer-scale behavior)
4161 template<UnitType UnitTypeLhs, DimensionlessUnitType D>
4163 constexpr UnitTypeLhs& operator*=(UnitTypeLhs& lhs, const D& rhs)
4164 {
4165 return (lhs *= rhs.value());
4166 }
4167
4168 template<UnitType UnitTypeLhs, ArithmeticType T>
4170 constexpr UnitTypeLhs& operator/=(UnitTypeLhs& lhs, const T& rhs)
4171 {
4172 // see operator*= above: a floating-point divisor narrowing an integer-underlying quantity surfaces
4173 // -Wfloat-conversion via the implicit narrow into a local of the lhs underlying type
4174 typename UnitTypeLhs::underlying_type scaled = lhs.raw() / rhs;
4175 lhs = UnitTypeLhs(scaled, linearized_value);
4176 return lhs;
4177 }
4178
4179 template<UnitType UnitTypeLhs, DimensionlessUnitType D>
4181 constexpr UnitTypeLhs& operator/=(UnitTypeLhs& lhs, const D& rhs)
4182 {
4183 return (lhs /= rhs.value());
4184 }
4185
4186 template<RatioDimensionlessUnitType U, RatioDimensionlessUnitType URhs>
4187 requires(traits::has_linear_scale_v<U, URhs>)
4188 constexpr U& operator/=(U& lhs, const URhs& rhs) noexcept
4189 {
4190 using Under0 = std::common_type_t<typename U::underlying_type, typename URhs::underlying_type>;
4191 using Under = detail::floating_point_promotion_t<Under0>;
4192
4193 const Under rhs_frac = static_cast<Under>(rhs.value()); // normalized fraction
4194
4195 const Under new_points = static_cast<Under>(lhs.raw()) / rhs_frac;
4196
4197 lhs = U(new_points);
4198 return lhs;
4199 }
4200
4201 template<RatioDimensionlessUnitType U>
4202 requires(units::traits::has_linear_scale_v<U>)
4203 constexpr U& operator/=(U& lhs, const U& rhs) noexcept
4204 {
4205 using Underlying = typename U::underlying_type;
4206 using R = typename U::conversion_factor::conversion_ratio;
4207
4208 constexpr long double points_per_one = static_cast<long double>(R::den) / static_cast<long double>(R::num);
4209
4210 const long double lhs_frac = static_cast<long double>(lhs.value());
4211 const long double rhs_frac = static_cast<long double>(rhs.value());
4212
4213 const long double out_frac = lhs_frac / rhs_frac;
4214 const long double out_points = out_frac * points_per_one;
4215
4216 if constexpr (std::is_integral_v<Underlying>)
4217 {
4218 lhs = U(static_cast<Underlying>(std::llround(out_points)));
4219 }
4220 else
4221 {
4222 lhs = U(static_cast<Underlying>(out_points));
4223 }
4224 return lhs;
4225 }
4226
4227 template<RatioDimensionlessUnitType U, units::ArithmeticType T>
4228 requires(units::traits::has_linear_scale_v<U>)
4229 constexpr U& operator/=(U& lhs, T rhs) noexcept
4230 {
4231 using Underlying = typename U::underlying_type;
4232 using R = typename U::conversion_factor::conversion_ratio;
4233
4234 constexpr long double points_per_one = static_cast<long double>(R::den) / static_cast<long double>(R::num);
4235
4236 const long double lhs_frac = static_cast<long double>(lhs.value());
4237 const long double out_frac = lhs_frac / static_cast<long double>(rhs);
4238 const long double out_pts = out_frac * points_per_one;
4239
4240 if constexpr (std::is_integral_v<Underlying>)
4241 {
4242 lhs = U(static_cast<Underlying>(std::llround(out_pts)));
4243 }
4244 else
4245 {
4246 lhs = U(static_cast<Underlying>(out_pts));
4247 }
4248 return lhs;
4249 }
4250
4251 template<RatioDimensionlessUnitType U, DimensionlessUnitType D>
4252 requires(units::traits::has_linear_scale_v<U, D> && !RatioDimensionlessUnitType<D>)
4253 constexpr U& operator/=(U& lhs, const D& rhs) noexcept
4254 {
4255 return (lhs /= rhs.value());
4256 }
4257
4258 template<DimensionedUnitType UnitTypeLhs>
4260 constexpr UnitTypeLhs& operator%=(UnitTypeLhs& lhs, const detail::type_identity_t<UnitTypeLhs>& rhs) noexcept
4261 {
4262 lhs = lhs % rhs;
4263 return lhs;
4264 }
4265
4266 template<DimensionlessUnitType UnitTypeLhs, DimensionlessUnitType UnitTypeRhs>
4268 constexpr UnitTypeLhs& operator%=(UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4269 {
4270 using CommonUnit = decltype(lhs % rhs);
4271 lhs = CommonUnit(lhs.raw() % rhs.raw());
4272 return lhs;
4273 }
4274
4275 template<UnitType UnitTypeLhs>
4277 constexpr UnitTypeLhs& operator%=(UnitTypeLhs& lhs, const typename UnitTypeLhs::underlying_type& rhs) noexcept
4278 {
4279 lhs = lhs % rhs;
4280 return lhs;
4281 }
4282
4283 // ratio-dimensionless %= ratio-dimensionless (percent points modulo percent points)
4284 template<RatioDimensionlessUnitType U>
4285 requires(traits::has_linear_scale_v<U> && IntegralUnitType<U>)
4286 constexpr U& operator%=(U& lhs, const U& rhs) noexcept
4287 {
4288 lhs = lhs % rhs;
4289 return lhs;
4290 }
4291
4292 // ratio-dimensionless %= scalar (percent points modulo scalar)
4293 template<RatioDimensionlessUnitType U>
4294 requires(traits::has_linear_scale_v<U> && IntegralUnitType<U>)
4295 constexpr U& operator%=(U& lhs, const typename U::underlying_type& rhs) noexcept
4296 {
4297 lhs = lhs % rhs;
4298 return lhs;
4299 }
4300
4301 // ratio-dimensionless %= base dimensionless unit (treat as scalar)
4302 template<RatioDimensionlessUnitType U, DimensionlessUnitType D>
4303 requires(traits::has_linear_scale_v<U, D> && !RatioDimensionlessUnitType<D> && IntegralUnitType<U> && IntegralUnitType<D>)
4304 constexpr U& operator%=(U& lhs, const D& rhs) noexcept
4305 {
4306 // D is ordinary dimensionless: safe scalar conversion
4307 lhs = lhs % static_cast<typename U::underlying_type>(rhs);
4308 return lhs;
4309 }
4310
4311 // Two DIFFERENT ratio-scaled dimensionless units (percent and parts-per-million, say) count in different ticks, so
4312 // the modulo of their point counts has no meaning. These deleted overloads catch that mix explicitly -- otherwise
4313 // the right operand would implicitly convert into the left's unit and silently bind the same-unit overload -- and
4314 // give a clean "use of deleted function" diagnostic instead. Use a common scale or `fmod` on the physical value.
4315 template<RatioDimensionlessUnitType U, RatioDimensionlessUnitType V>
4316 requires(!std::is_same_v<U, V>)
4317 constexpr std::common_type_t<U, V> operator%(const U&, const V&) = delete;
4318 template<RatioDimensionlessUnitType U, RatioDimensionlessUnitType V>
4319 requires(!std::is_same_v<U, V>)
4320 constexpr U& operator%=(U&, const V&) = delete;
4321
4322 //------------------------------
4323 // UNIT UNARY OPERATORS
4324 //------------------------------
4325
4326 // unary addition: +T
4327 template<UnitType UnitTypeLhs>
4328 constexpr UnitTypeLhs operator+(const UnitTypeLhs& u) noexcept
4329 {
4330 return u;
4331 }
4332
4333 // prefix increment: ++T
4334 template<UnitType UnitTypeLhs>
4335 constexpr UnitTypeLhs& operator++(UnitTypeLhs& u) noexcept
4336 {
4337 u = UnitTypeLhs(u.raw() + 1);
4338 return u;
4339 }
4340
4341 // postfix increment: T++
4342 template<UnitType UnitTypeLhs>
4343 constexpr UnitTypeLhs operator++(UnitTypeLhs& u, int) noexcept
4344 {
4345 auto ret = u;
4346 u = UnitTypeLhs(u.raw() + 1);
4347 return ret;
4348 }
4349
4350 // unary addition: -T
4351 template<UnitType UnitTypeLhs>
4352 constexpr UnitTypeLhs operator-(const UnitTypeLhs& u) noexcept
4353 {
4354 return UnitTypeLhs(-u.raw());
4355 }
4356
4357 // prefix increment: --T
4358 template<UnitType UnitTypeLhs>
4359 constexpr UnitTypeLhs& operator--(UnitTypeLhs& u) noexcept
4360 {
4361 u = UnitTypeLhs(u.raw() - 1);
4362 return u;
4363 }
4364
4365 // postfix increment: T--
4366 template<UnitType UnitTypeLhs>
4367 constexpr UnitTypeLhs operator--(UnitTypeLhs& u, int) noexcept
4368 {
4369 auto ret = u;
4370 u = UnitTypeLhs(u.raw() - 1);
4371 return ret;
4372 }
4373
4374 //------------------------------
4375 // LINEAR ARITHMETIC
4376 //------------------------------
4377
4383 /// physically intended operation.
4384 /// @details The result is expressed in the LEFT operand's unit, so the caller controls the result unit by
4385 /// operand order (`meters + feet` is meters, `feet + meters` is feet) and the value reads in the unit
4386 /// they named. The underlying is widened only when the left operand's is integral and the right cannot
4387 /// convert into it without truncation, in which case the result reconciles to the common (finest,
4388 /// lossless) unit — the same exact behavior integer comparisons rely on.
4389 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
4390 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> &&
4392 constexpr auto operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4393 {
4394 // The result unit is computed in the body (not the signature) so the trait is never instantiated for a
4395 // non-unit operand that the constraint above already rejects — a stricter compiler evaluates a trailing
4396 // return type during overload resolution and would otherwise hard-error on, e.g., a vector iterator's
4397 // pointer subtraction that briefly considers this operator.
4398 using ResultUnit = detail::lhs_result_unit_t<UnitTypeLhs, UnitTypeRhs>;
4399 return ResultUnit(ResultUnit(lhs).raw() + ResultUnit(rhs).raw());
4400 }
4401
4403 template<RatioDimensionlessUnitType U, ArithmeticType T>
4404 requires(traits::has_linear_scale_v<U>)
4405 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>>>
4406 {
4407 using Under0 = std::common_type_t<typename U::underlying_type, T>;
4408 using Under = detail::floating_point_promotion_t<Under0>;
4409 using Ret = traits::replace_underlying_t<U, Under>;
4410
4411 using R = typename U::conversion_factor::conversion_ratio; // e.g. percent: 1/100
4412 constexpr Under points_per_one = static_cast<Under>(R::den) / static_cast<Under>(R::num);
4413
4414 // fraction-space math, then back to points
4415 const Under frac = static_cast<Under>(lhs.value()) + static_cast<Under>(rhs);
4416 return Ret(frac * points_per_one);
4417 }
4418
4419 template<RatioDimensionlessUnitType U, ArithmeticType T>
4420 requires(traits::has_linear_scale_v<U>)
4421 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>>>
4422 {
4423 using Under0 = std::common_type_t<T, typename U::underlying_type>;
4424 using Under = detail::floating_point_promotion_t<Under0>;
4425 using Ret = traits::replace_underlying_t<U, Under>;
4426
4427 using R = typename U::conversion_factor::conversion_ratio;
4428 constexpr Under points_per_one = static_cast<Under>(R::den) / static_cast<Under>(R::num);
4430 const Under frac = static_cast<Under>(lhs) + static_cast<Under>(rhs.value());
4431 return Ret(frac * points_per_one);
4432 }
4433
4434
4436 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4437 requires(traits::has_linear_scale_v<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeLhs>)
4438 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>> operator+(const UnitTypeLhs& lhs, T rhs) noexcept
4440 using ret = traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>>;
4441 return ret(lhs.raw() + static_cast<ret::underlying_type>(rhs));
4442 }
4443
4446 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4447 requires(traits::has_linear_scale_v<UnitTypeRhs> && !RatioDimensionlessUnitType<UnitTypeRhs>)
4448 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<T, typename UnitTypeRhs::underlying_type>> operator+(T lhs, const UnitTypeRhs& rhs) noexcept
4449 {
4450 // Apply any necessary scale factor to T using multiplication for lossless conversion
4451 // for non-scaled dimensionless units it's a no-op
4452 using CommonUnit = decltype(lhs + rhs);
4453 using InverseCommonUnit = decltype(1 / CommonUnit(1));
4454 return CommonUnit(InverseCommonUnit(lhs).value() + rhs.raw());
4456
4461 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
4462 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> &&
4464 constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4465 {
4466 // Result unit computed in the body, not the signature — see operator+ above.
4467 using ResultUnit = detail::lhs_result_unit_t<UnitTypeLhs, UnitTypeRhs>;
4468 return ResultUnit(ResultUnit(lhs).raw() - ResultUnit(rhs).raw());
4469 }
4470
4472 /// @details The difference of two absolute affine quantities is a DELTA: the datum offsets cancel, so
4473 /// the result must be a pure (non-affine) quantity — otherwise storing it back into an affine
4474 /// unit would re-apply the offset (e.g. celsius(0) - kelvin(0) would read 546.30 K instead of
4475 /// the true 273.15 K delta). Both operands are reconciled to their common affine unit, their
4476 /// raw values subtracted (the offsets cancel exactly), and the result returned in the
4477 /// offset-stripped counterpart of that common unit so it never re-applies a datum.
4478 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
4479 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> &&
4481 constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4482 {
4483 // Reconcile to the LEFT operand's affine unit (its datum applied to the right operand as it converts),
4484 // so the delta is expressed in the left operand's scale — celsius(100) - fahrenheit(32) is 100 celsius
4485 // degrees, not a value in an anonymous sub-unit of the two scales' common measure. The result is the
4486 // offset-STRIPPED counterpart of the left unit so no datum is ever re-applied to the delta.
4487 using LhsCf = typename traits::unit_traits<UnitTypeLhs>::conversion_factor;
4489 typename traits::conversion_factor_traits<LhsCf>::dimension_type,
4490 typename traits::conversion_factor_traits<LhsCf>::pi_exponent_ratio, std::ratio<0>>;
4491 using DeltaUnit = unit<traits::strong_t<DeltaCf>, typename UnitTypeLhs::underlying_type, typename UnitTypeLhs::numerical_scale_type>;
4492 return DeltaUnit(lhs.raw() - UnitTypeLhs(rhs).raw());
4493 }
4494
4497 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4498 requires(traits::has_linear_scale_v<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeLhs>)
4499 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>> operator-(const UnitTypeLhs& lhs, T rhs) noexcept
4500 {
4501 // Apply any necessary scale factor to T using multiplication for lossless conversion
4502 // for non-scaled dimensionless units it's a no-op
4503 using CommonUnit = decltype(lhs - rhs);
4504 using InverseCommonUnit = decltype(1 / CommonUnit(1));
4505 return CommonUnit(lhs.raw() - InverseCommonUnit(rhs).value());
4506 }
4507
4509 template<RatioDimensionlessUnitType U, ArithmeticType T>
4510 requires(traits::has_linear_scale_v<U>)
4511 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>>>
4512 {
4513 using Under0 = std::common_type_t<typename U::underlying_type, T>;
4514 using Under = detail::floating_point_promotion_t<Under0>;
4515 using Ret = traits::replace_underlying_t<U, Under>;
4516
4517 using R = typename U::conversion_factor::conversion_ratio;
4518 constexpr Under points_per_one = static_cast<Under>(R::den) / static_cast<Under>(R::num);
4519
4520 const Under frac = static_cast<Under>(lhs.value()) - static_cast<Under>(rhs);
4521 return Ret(frac * points_per_one);
4522 }
4523
4524 template<RatioDimensionlessUnitType U, ArithmeticType T>
4525 requires(traits::has_linear_scale_v<U>)
4526 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>>>
4527 {
4528 using Under0 = std::common_type_t<T, typename U::underlying_type>;
4529 using Under = detail::floating_point_promotion_t<Under0>;
4530 using Ret = traits::replace_underlying_t<U, Under>;
4531
4532 using R = typename U::conversion_factor::conversion_ratio;
4533 constexpr Under points_per_one = static_cast<Under>(R::den) / static_cast<Under>(R::num);
4535 const Under frac = static_cast<Under>(lhs) - static_cast<Under>(rhs.value());
4536 return Ret(frac * points_per_one);
4537 }
4538
4541 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4542 requires(traits::has_linear_scale_v<UnitTypeRhs> && !RatioDimensionlessUnitType<UnitTypeRhs>)
4543 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<T, typename UnitTypeRhs::underlying_type>> operator-(T lhs, const UnitTypeRhs& rhs) noexcept
4544 {
4545 // Apply any necessary scale factor to T using multiplication for lossless conversion
4546 // for non-scaled dimensionless units it's a no-op
4547 using CommonUnit = decltype(lhs - rhs);
4548 using InverseCommonUnit = decltype(1 / CommonUnit(1));
4549 return CommonUnit(InverseCommonUnit(lhs).value() - rhs.raw());
4550 }
4551
4554 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
4555 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4556 constexpr auto operator*(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4557 -> detail::rewrap_to_named_t<unit<traits::strong_t<squared<typename traits::unit_traits<std::common_type_t<UnitTypeLhs, UnitTypeRhs>>::conversion_factor>>,
4558 typename std::common_type_t<UnitTypeLhs, UnitTypeRhs>::underlying_type>>
4559 {
4560 using SquaredUnit = decltype(lhs * rhs);
4561 using CommonUnit = std::common_type_t<UnitTypeLhs, UnitTypeRhs>;
4562 return SquaredUnit(CommonUnit(lhs).raw() * CommonUnit(rhs).raw());
4563 }
4564
4567 template<DimensionedUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4568 requires(!same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4569 constexpr auto operator*(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4570 -> 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>>,
4571 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>>>
4573 using CompoundUnit = decltype(lhs * rhs);
4574 using CommonUnderlying = typename CompoundUnit::underlying_type;
4575 return CompoundUnit(static_cast<CommonUnderlying>(lhs) * static_cast<CommonUnderlying>(rhs));
4576 }
4577
4578
4579 template<DimensionedUnitType UnitTypeLhs, OrdinaryDimensionlessUnitType UnitTypeRhs>
4580 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4581 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator*(
4582 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4584 using CommonUnit = decltype(lhs * rhs);
4585 return CommonUnit(CommonUnit(lhs).raw() * static_cast<typename CommonUnit::underlying_type>(rhs));
4586 }
4587
4590 template<DimensionedUnitType UnitTypeLhs, RatioDimensionlessUnitType UnitTypeRhs>
4591 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4592 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator*(
4593 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4594 {
4595 using Out = decltype(lhs * rhs);
4596 using U0 = std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>;
4597 using U = detail::floating_point_promotion_t<U0>;
4598
4599 // rhs.value() is normalized fraction (e.g. 200_pct -> 2.0, 50_ppb -> 50e-9)
4600 return Out(static_cast<U>(lhs.raw()) * static_cast<U>(rhs.value()));
4601 }
4602
4603
4604 template<OrdinaryDimensionlessUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4605 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4606 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator*(
4607 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4608 {
4609 using CommonUnit = decltype(lhs * rhs);
4610 return CommonUnit(static_cast<typename CommonUnit::underlying_type>(lhs) * CommonUnit(rhs).raw());
4611 }
4612
4614 template<RatioDimensionlessUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4615 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4616 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator*(
4617 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4618 {
4619 using Out = decltype(lhs * rhs);
4620 using U0 = std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>;
4621 using U = detail::floating_point_promotion_t<U0>;
4622
4623 return Out(static_cast<U>(lhs.value()) * static_cast<U>(rhs.raw()));
4624 }
4625
4627 template<DimensionedUnitType UnitTypeLhs, ArithmeticType T>
4628 requires(traits::has_linear_scale_v<UnitTypeLhs>)
4629 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>> operator*(const UnitTypeLhs& lhs, T rhs) noexcept
4630 {
4631 using CommonUnit = decltype(lhs * rhs);
4632 return CommonUnit(CommonUnit(lhs).raw() * rhs);
4633 }
4634
4636 template<DimensionedUnitType UnitTypeRhs, ArithmeticType T>
4637 requires(traits::has_linear_scale_v<UnitTypeRhs>)
4638 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<T, typename UnitTypeRhs::underlying_type>> operator*(T lhs, const UnitTypeRhs& rhs) noexcept
4639 {
4640 using CommonUnit = decltype(lhs * rhs);
4641 return CommonUnit(lhs * CommonUnit(rhs).raw());
4642 }
4643
4645 template<RatioDimensionlessUnitType U, units::ArithmeticType T>
4646 requires(units::traits::has_linear_scale_v<U>)
4647 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
4648 {
4649 using Under0 = std::common_type_t<T, typename U::underlying_type>;
4650 using Under = units::detail::floating_point_promotion_t<Under0>;
4651
4652 // rhs converts to Under as normalized fraction (e.g. 50_pct -> 0.5)
4653 return units::dimensionless<Under>(static_cast<Under>(lhs) * static_cast<Under>(rhs));
4654 }
4655
4657 template<RatioDimensionlessUnitType U, units::ArithmeticType T>
4658 requires(units::traits::has_linear_scale_v<U>)
4659 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
4660 {
4661 using Under0 = std::common_type_t<typename U::underlying_type, T>;
4662 using Under = units::detail::floating_point_promotion_t<Under0>;
4663
4664 return units::dimensionless<Under>(static_cast<Under>(lhs) * static_cast<Under>(rhs));
4665 }
4666
4668 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4669 requires(traits::has_linear_scale_v<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeLhs>)
4670 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>> operator*(const UnitTypeLhs& lhs, T rhs) noexcept
4671 {
4672 using CommonUnit = decltype(lhs * rhs);
4673 return CommonUnit(lhs.raw() * rhs);
4674 }
4675
4677 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4678 requires(traits::has_linear_scale_v<UnitTypeRhs> && !RatioDimensionlessUnitType<UnitTypeRhs>)
4679 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<T, typename UnitTypeRhs::underlying_type>> operator*(T lhs, const UnitTypeRhs& rhs) noexcept
4680 {
4681 using CommonUnit = decltype(lhs * rhs);
4682 return CommonUnit(lhs * rhs.raw());
4683 }
4684
4686
4687 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
4688 requires(
4689 same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> && !RatioDimensionlessUnitType<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeRhs>)
4690 constexpr dimensionless<std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4692 using CommonUnit = std::common_type_t<UnitTypeLhs, UnitTypeRhs>;
4693 return CommonUnit(lhs).raw() / CommonUnit(rhs).raw();
4694 }
4695
4698 template<DimensionedUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4699 requires(!same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4700 constexpr auto operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4701 -> 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>>>,
4702 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>>>
4704 using CompoundUnit = decltype(lhs / rhs);
4705 using CommonUnderlying = typename CompoundUnit::underlying_type;
4706 return CompoundUnit(static_cast<CommonUnderlying>(lhs) / static_cast<CommonUnderlying>(rhs));
4707 }
4708
4710 template<DimensionedUnitType UnitTypeLhs, OrdinaryDimensionlessUnitType UnitTypeRhs>
4711 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4712 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator/(
4713 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4714 {
4715 using CommonUnit = decltype(lhs / rhs);
4716 using CommonUnderlying = typename CommonUnit::underlying_type;
4717
4718 // Ordinary dimensionless is a true scalar
4719 return CommonUnit(CommonUnit(lhs).raw() / static_cast<CommonUnderlying>(rhs));
4720 }
4724 template<DimensionedUnitType UnitTypeLhs, RatioDimensionlessUnitType UnitTypeRhs>
4725 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4726 constexpr traits::replace_underlying_t<
4727 UnitTypeLhs,
4728 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>
4729 >
4730 operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4731 {
4732 using Out = decltype(lhs / rhs);
4733 using U0 = std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>;
4734 using U = detail::floating_point_promotion_t<U0>;
4735
4736 return Out(static_cast<U>(lhs.raw()) / static_cast<U>(rhs.value()));
4737 }
4738
4741 template<OrdinaryDimensionlessUnitType UnitTypeLhs, RatioDimensionlessUnitType UnitTypeRhs>
4742 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4743 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>>,
4744 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>>>
4745 {
4746 using Out = decltype(lhs / rhs);
4747 using CommonUnderlying = typename Out::underlying_type;
4748
4749 // lhs is true scalar, rhs is points (not scalar fraction)
4750 return Out(static_cast<CommonUnderlying>(lhs) / static_cast<CommonUnderlying>(rhs.raw()));
4751 }
4752
4754 template<OrdinaryDimensionlessUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4755 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> && traits::is_dimensionless_unit_v<UnitTypeLhs>)
4756 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>>,
4757 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>>>
4758 {
4759 using CommonUnit = decltype(lhs / rhs);
4760 using CommonUnderlying = typename CommonUnit::underlying_type;
4761 return CommonUnit(static_cast<CommonUnderlying>(lhs) / static_cast<CommonUnderlying>(rhs));
4762 }
4763
4766 template<RatioDimensionlessUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4767 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
4768 constexpr auto operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4769 -> unit<
4770 traits::strong_t<
4772 typename traits::unit_traits<UnitTypeLhs>::conversion_factor,
4773 inverse<typename traits::unit_traits<UnitTypeRhs>::conversion_factor>
4774 >
4775 >,
4776 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>
4777 >
4778 {
4779 using Out = decltype(lhs / rhs);
4780 using CommonUnderlying = typename Out::underlying_type;
4781
4782 // numeric part: ppb points / years -> "ppb per year" numeric value
4783 // keep lhs as points (raw), keep rhs in its own units (raw)
4784 return Out(
4785 static_cast<CommonUnderlying>(lhs.raw()) / static_cast<CommonUnderlying>(rhs.raw()),
4786 linearized_value
4787 );
4788 }
4789
4791 template<UnitType UnitTypeLhs, ArithmeticType T>
4792 requires(traits::has_linear_scale_v<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeLhs>)
4793 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>> operator/(const UnitTypeLhs& lhs, T rhs) noexcept
4794 {
4795 using CommonUnit = decltype(lhs / rhs);
4796 return CommonUnit(CommonUnit(lhs).raw() / rhs);
4797 }
4798
4800 template<UnitType UnitTypeRhs, ArithmeticType T>
4801 requires(traits::has_linear_scale_v<UnitTypeRhs> && !RatioDimensionlessUnitType<UnitTypeRhs>)
4802 constexpr auto operator/(T lhs, const UnitTypeRhs& rhs) noexcept
4803 -> 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>>>
4804 {
4805 using InverseUnit = decltype(lhs / rhs);
4806 using UnitConversion = typename traits::unit_traits<UnitTypeRhs>::conversion_factor;
4807 using CommonUnderlying = std::common_type_t<T, typename UnitTypeRhs::underlying_type>;
4808 using CommonUnit = unit<UnitConversion, CommonUnderlying>;
4809 return InverseUnit(lhs / CommonUnit(rhs).raw());
4810 }
4811
4812
4813 // U / scalar -> U (percent points divided, still percent)
4814 template<RatioDimensionlessUnitType U, ArithmeticType T>
4815 requires(traits::has_linear_scale_v<U>)
4816 constexpr traits::replace_underlying_t<U, std::common_type_t<typename U::underlying_type, T>> operator/(const U& lhs, T rhs) noexcept
4817 {
4818 using Out = traits::replace_underlying_t<U, std::common_type_t<typename U::underlying_type, T>>;
4819 return Out(Out(lhs).raw() / rhs);
4820 }
4821
4822 // scalar / ratio-dimensionless -> dimensionless (normalized)
4823 template<RatioDimensionlessUnitType U, ArithmeticType T>
4824 requires(traits::has_linear_scale_v<U>)
4825 constexpr units::dimensionless<detail::floating_point_promotion_t<std::common_type_t<T, typename U::underlying_type>>> operator/(T lhs, const U& rhs) noexcept
4826 {
4827 using CommonType = std::common_type_t<T, typename U::underlying_type>;
4828 using PromotedType = detail::floating_point_promotion_t<CommonType>;
4829
4830 // rhs.value() is normalized fraction (e.g. 50_pct -> 0.5)
4831 return units::dimensionless<PromotedType>(static_cast<PromotedType>(lhs) / static_cast<PromotedType>(rhs.value()));
4832 }
4833
4834 // U / U -> dimensionless (normalized)
4835 template<RatioDimensionlessUnitType U1, RatioDimensionlessUnitType U2>
4836 requires(traits::has_linear_scale_v<U1, U2>)
4837 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
4838 {
4839 using Under0 = std::common_type_t<typename U1::underlying_type, typename U2::underlying_type>;
4840 using Under = detail::floating_point_promotion_t<Under0>;
4841 return dimensionless<Under>(static_cast<Under>(lhs.value()) / static_cast<Under>(rhs.value()));
4842 }
4843
4845 /// their common (finer) unit.
4846 /// @note The result is the `std::common_type` of the operands — the finer of the two units — not the
4847 /// lhs unit. Returning the lhs unit made the operator order-dependent: `meters % kilometers`
4848 /// compiled (finer lhs) but `kilometers % meters` did not (converting the finer common result
4849 /// back to the coarser lhs is lossy for an integer underlying, disabling the constructor). The
4850 /// common-unit result mirrors `fmod` and removes the asymmetry.
4851 template<DimensionedUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
4852 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> &&
4854 constexpr std::common_type_t<UnitTypeLhs, UnitTypeRhs> operator%(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4855 {
4856 using CommonUnit = std::common_type_t<UnitTypeLhs, UnitTypeRhs>;
4857 return CommonUnit(CommonUnit(lhs).raw() % CommonUnit(rhs).raw());
4858 }
4859
4861 template<DimensionedUnitType UnitTypeLhs, DimensionlessUnitType UnitTypeRhs>
4862 requires(traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> && IntegralUnitType<UnitTypeLhs> && IntegralUnitType<UnitTypeRhs>)
4863 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator%(
4864 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4865 {
4866 using CommonUnit = decltype(lhs % rhs);
4867 using CommonUnderlying = typename CommonUnit::underlying_type;
4868 return CommonUnit(CommonUnit(lhs).raw() % static_cast<CommonUnderlying>(rhs));
4869 }
4870
4876 /// meaning; that mix is excluded here (`!(RatioDimensionlessUnitType<UnitTypeLhs> &&
4877 /// RatioDimensionlessUnitType<UnitTypeRhs>)`, reinforced by a deleted overload for the convertible case)
4878 /// and does not compile, exactly as the ratio-dimensionless compound assignment excludes it. The permitted forms are two
4879 /// operands of the SAME unit (handled by the same-unit ratio overload) and a ratio-scaled unit modulo a
4880 /// plain `dimensionless` (a bare count), which this overload serves.
4881 template<DimensionlessUnitType UnitTypeLhs, DimensionlessUnitType UnitTypeRhs>
4882 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs> &&
4885 constexpr traits::replace_underlying_t<UnitTypeLhs, typename std::common_type_t<UnitTypeLhs, UnitTypeRhs>::underlying_type> operator%(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
4886 {
4887 using CommonUnit = decltype(lhs % rhs);
4888 return CommonUnit(lhs.raw() % rhs.raw());
4889 }
4890
4892 template<UnitType UnitTypeLhs, ArithmeticType T>
4893 requires(traits::has_linear_scale_v<UnitTypeLhs> && !RatioDimensionlessUnitType<UnitTypeLhs> && IntegralUnitType<UnitTypeLhs> && std::integral<T>)
4894 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, T>> operator%(const UnitTypeLhs& lhs, const T& rhs) noexcept
4895 {
4896 using CommonUnit = decltype(lhs % rhs);
4897 return CommonUnit(CommonUnit(lhs).raw() % rhs);
4898 }
4899
4900 // Modulos for ratio-like dimensionless units
4901 template<RatioDimensionlessUnitType U>
4902 requires(traits::has_linear_scale_v<U> && IntegralUnitType<U>)
4903 constexpr U operator%(const U& lhs, const U& rhs) noexcept
4904 {
4905 return U(lhs.raw() % rhs.raw());
4906 }
4907
4908 template<RatioDimensionlessUnitType U, ArithmeticType T>
4909 requires(traits::has_linear_scale_v<U> && IntegralUnitType<U> && std::integral<T>)
4910 constexpr U operator%(const U& lhs, T rhs) noexcept
4911 {
4912 return U(lhs.raw() % rhs);
4913 }
4914
4915 template<RatioDimensionlessUnitType U, ArithmeticType T>
4916 requires(traits::has_linear_scale_v<U> && IntegralUnitType<U> && std::integral<T>)
4917 constexpr U operator%(T lhs, const U& rhs) noexcept
4918 {
4919 using Under = detail::floating_point_promotion_t<std::common_type_t<T, typename U::underlying_type>>;
4920 // If lhs is integral, keep integer modulo semantics
4921 if constexpr (std::is_integral_v<T> && std::is_integral_v<typename U::underlying_type>)
4922 return U(lhs % rhs.raw());
4923 else
4924 return U(static_cast<Under>(std::fmod(static_cast<Under>(lhs), static_cast<Under>(rhs.raw()))));
4925 }
4926
4927 //----------------------------------
4928 // DIMENSIONLESS COMPARISONS
4929 //----------------------------------
4930
4931 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4932 constexpr bool operator==(const T& lhs, const UnitTypeRhs& rhs) noexcept
4933 {
4934 using CommonUnderlying = std::common_type_t<T, typename UnitTypeRhs::underlying_type>;
4935
4936 const auto common_lhs = static_cast<CommonUnderlying>(lhs);
4937 const auto common_rhs = static_cast<CommonUnderlying>(rhs);
4938
4939 if constexpr (std::is_integral_v<CommonUnderlying>)
4940 {
4941 return common_lhs == common_rhs;
4942 }
4943 else
4944 {
4945 return abs(common_lhs - common_rhs) < std::numeric_limits<CommonUnderlying>::epsilon() * abs(common_lhs + common_rhs) ||
4946 abs(common_lhs - common_rhs) < std::numeric_limits<CommonUnderlying>::min();
4947 }
4948 }
4949
4950 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4951 constexpr bool operator==(const UnitTypeLhs& lhs, const T& rhs) noexcept
4952 {
4953 return rhs == lhs;
4954 }
4955
4956 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4957 requires(traits::is_dimensionless_unit_v<UnitTypeRhs> && std::is_arithmetic_v<T>)
4958 constexpr bool operator!=(const T& lhs, const UnitTypeRhs& rhs) noexcept
4959 {
4960 return !(lhs == rhs);
4961 }
4962
4963 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4964 constexpr bool operator!=(const UnitTypeLhs& lhs, const T& rhs) noexcept
4965 {
4966 return !(lhs == rhs);
4967 }
4968
4969 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4970 requires(traits::is_dimensionless_unit_v<UnitTypeRhs> && std::is_arithmetic_v<T>)
4971 constexpr bool operator>=(const T& lhs, const UnitTypeRhs& rhs) noexcept
4972 {
4973 using CommonUnderlying = std::common_type_t<T, typename UnitTypeRhs::underlying_type>;
4974 return lhs >= static_cast<CommonUnderlying>(rhs);
4975 }
4976
4977 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4978 constexpr bool operator>=(const UnitTypeLhs& lhs, const T& rhs) noexcept
4979 {
4980 using CommonUnderlying = std::common_type_t<typename UnitTypeLhs::underlying_type, T>;
4981 return static_cast<CommonUnderlying>(lhs) >= rhs;
4982 }
4983
4984 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4985 constexpr bool operator>(const T& lhs, const UnitTypeRhs& rhs) noexcept
4986 {
4987 using CommonUnderlying = std::common_type_t<T, typename UnitTypeRhs::underlying_type>;
4988 return lhs > static_cast<CommonUnderlying>(rhs);
4989 }
4990
4991 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
4992 constexpr bool operator>(const UnitTypeLhs& lhs, const T& rhs) noexcept
4993 {
4994 using CommonUnderlying = std::common_type_t<typename UnitTypeLhs::underlying_type, T>;
4995 return static_cast<CommonUnderlying>(lhs) > rhs;
4996 }
4997
4998 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
4999 constexpr bool operator<=(const T& lhs, const UnitTypeRhs& rhs) noexcept
5000 {
5001 using CommonUnderlying = std::common_type_t<T, typename UnitTypeRhs::underlying_type>;
5002 return lhs <= static_cast<CommonUnderlying>(rhs);
5003 }
5004
5005 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
5006 constexpr bool operator<=(const UnitTypeLhs& lhs, const T& rhs) noexcept
5007 {
5008 using CommonUnderlying = std::common_type_t<typename UnitTypeLhs::underlying_type, T>;
5009 return static_cast<CommonUnderlying>(lhs) <= rhs;
5010 }
5011
5012 template<DimensionlessUnitType UnitTypeRhs, ArithmeticType T>
5013 constexpr bool operator<(const T& lhs, const UnitTypeRhs& rhs) noexcept
5014 {
5015 using CommonUnderlying = std::common_type_t<T, typename UnitTypeRhs::underlying_type>;
5016 return lhs < static_cast<CommonUnderlying>(rhs);
5017 }
5018
5019 template<DimensionlessUnitType UnitTypeLhs, ArithmeticType T>
5020 constexpr bool operator<(const UnitTypeLhs& lhs, const T& rhs) noexcept
5021 {
5022 using CommonUnderlying = std::common_type_t<typename UnitTypeLhs::underlying_type, T>;
5023 return static_cast<CommonUnderlying>(lhs) < rhs;
5024 }
5025
5026 //----------------------------------
5027 // POW
5028 //----------------------------------
5029 // DOXYGEN IGNORE
5031 namespace detail
5032 {
5034 template<int N, class U>
5035 struct power_of_unit
5036 {
5037 template<bool isPos, int V>
5038 struct power_of_unit_impl;
5039
5040 template<int V>
5041 struct power_of_unit_impl<true, V>
5042 {
5043 typedef unit_multiply<U, typename power_of_unit<N - 1, U>::type> type;
5044 };
5045
5046 template<int V>
5047 struct power_of_unit_impl<false, V>
5048 {
5049 typedef inverse<typename power_of_unit<-N, U>::type> type;
5050 };
5051
5052 typedef typename power_of_unit_impl<(N > 0), N>::type type;
5053 };
5054
5056 template<class U>
5057 struct power_of_unit<1, U>
5058 {
5059 typedef U type;
5060 };
5061
5062 template<class U>
5063 struct power_of_unit<0, U>
5064 {
5065 typedef dimensionless_ type;
5066 };
5067 } // namespace detail // END DOXYGEN IGNORE
5069
5071 * @brief computes the value of <i>value</i> raised to the <i>power</i>
5072 * @details Only implemented for linear_scale units. <i>Power</i> must be known at compile time, so the
5073 * resulting unit type can be deduced.
5074 * @tparam power exponential power to raise <i>value</i> by.
5075 * @param[in] value `unit` derived type to raise to the given <i>power</i>
5076 * @returns new unit, raised to the given exponent
5077 */
5078 template<int power, UnitType UnitType>
5079 requires(traits::has_linear_scale_v<UnitType>)
5080 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>,
5081 detail::floating_point_promotion_t<typename units::traits::unit_traits<UnitType>::underlying_type>, linear_scale>>
5082 {
5083 return decltype(units::pow<power>(value))(pow<power>(value.raw()));
5084 }
5085
5086 //------------------------------
5087 // DECIBEL SCALE
5088 //------------------------------
5089
5096 {
5103 template<class T>
5104 static T linearize(const T value) noexcept
5105 {
5106 // A decibel value is stored through a base-10 logarithm, so an integral underlying type cannot
5107 // represent it: most decibel figures round to a wrong integer (3 dB stores as 0) and large ones
5108 // overflow. Asserted here, at the point a value is actually stored, so merely naming a
5109 // decibel-scale type for trait/overload resolution does not trip it.
5110 static_assert(std::is_floating_point_v<T>,
5111 "a decibel-scale unit requires a floating-point underlying type (an integral type cannot represent a logarithmic value)");
5112 return static_cast<T>(std::pow(10, value / 10));
5114
5117
5118
5121 template<class T>
5122 static T scale(const T value) noexcept
5123 {
5124 return static_cast<T>(10 * std::log10(value));
5125 }
5126 };
5128 //------------------------------
5129 // dimensionless (DECIBEL) UNITS
5130 //------------------------------
5131
5137#if !defined(UNIT_LIB_DISABLE_IOSTREAM)
5138 template<class Underlying>
5139 std::ostream& operator<<(std::ostream& os, const decibels<Underlying>& obj)
5140 {
5141 os << obj.raw() << " dB";
5142 return os;
5143 }
5144#endif
5145 template<class Underlying>
5146 using dBi = decibels<Underlying>;
5147
5148 // Register the name/abbreviation for the dimensionless decibel and its `_dB` literal. The reverse
5149 // named-class map is keyed on (conversion_factor, scale); the (dimensionless, decibel_scale) key
5150 // belongs to `decibels` alone (the power dB units use the watts/milliwatts factors), so the mapping
5151 // is unambiguous and the member name()/abbreviation() resolve through it.
5152 template<class Underlying>
5153 struct unit_name<decibels<Underlying>>
5154 {
5155 static constexpr const char* value = "decibels";
5156 };
5157
5158 template<class Underlying>
5159 struct unit_abbreviation<decibels<Underlying>>
5160 {
5161 static constexpr const char* value = "dB";
5162 };
5163
5164 namespace detail
5165 {
5167 typename ::units::decibels<>::conversion_factor*, typename ::units::decibels<>::numerical_scale_type*);
5168 }
5169
5170#ifndef UNIT_NO_LITERAL_SUPPORT
5171 namespace literals
5172 {
5173 // only a floating-point literal: a decibel scale requires a floating-point underlying type
5174 constexpr decibels<double> operator""_dB(long double d) noexcept
5175 {
5176 return decibels<double>(static_cast<double>(d));
5177 }
5178 } // namespace literals
5179#endif
5180
5181 //------------------------------
5182 // DECIBEL ARITHMETIC
5183 //------------------------------
5184
5190 /// (two equal powers sum to +3 dB), not by adding their dB numbers.
5191 template<DimensionedUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
5192 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5193 auto operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept = delete;
5194
5195
5197 template<DimensionlessUnitType UnitTypeLhs, DimensionlessUnitType UnitTypeRhs>
5198 requires(traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5199 constexpr std::common_type_t<UnitTypeLhs, UnitTypeRhs> operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
5200 {
5201 using CommonUnit = std::common_type_t<UnitTypeLhs, UnitTypeRhs>;
5202 return CommonUnit(CommonUnit(lhs).to_linearized() * CommonUnit(rhs).to_linearized(), linearized_value);
5203 }
5204
5205
5206 template<DimensionedUnitType UnitTypeLhs, DimensionlessUnitType UnitTypeRhs>
5207 requires(traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5208 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator+(
5209 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
5210 {
5211 using CommonUnit = decltype(lhs + rhs);
5212 return CommonUnit(lhs.to_linearized() * rhs.to_linearized(), linearized_value);
5213 }
5214
5215
5216 template<DimensionlessUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
5217 requires(traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5218 constexpr traits::replace_underlying_t<UnitTypeRhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator+(
5219 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
5220 {
5221 using CommonUnit = decltype(lhs + rhs);
5222 return CommonUnit(lhs.to_linearized() * rhs.to_linearized(), linearized_value);
5223 }
5224
5226 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5227 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5228 constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> decibels<typename std::common_type_t<UnitTypeLhs, UnitTypeRhs>::underlying_type>
5229 {
5230 using Dimensionless = decltype(lhs - rhs);
5231 using CommonUnit = std::common_type_t<UnitTypeLhs, UnitTypeRhs>;
5232
5233 return Dimensionless(CommonUnit(lhs).to_linearized() / CommonUnit(rhs).to_linearized(), linearized_value);
5234 }
5235
5236
5237 template<DimensionedUnitType UnitTypeLhs, DimensionlessUnitType UnitTypeRhs>
5238 requires(traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5239 constexpr traits::replace_underlying_t<UnitTypeLhs, std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>> operator-(
5240 const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
5241 {
5242 using CommonUnit = decltype(lhs - rhs);
5243 return CommonUnit(lhs.to_linearized() / rhs.to_linearized(), linearized_value);
5244 }
5245
5246
5247 template<DimensionlessUnitType UnitTypeLhs, DimensionedUnitType UnitTypeRhs>
5248 requires(traits::has_decibel_scale_v<UnitTypeLhs, UnitTypeRhs>)
5249 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>>,
5250 std::common_type_t<typename UnitTypeLhs::underlying_type, typename UnitTypeRhs::underlying_type>, decibel_scale>>
5251 {
5252 using InverseUnit = decltype(lhs - rhs);
5253 return InverseUnit(lhs.to_linearized() / rhs.to_linearized(), linearized_value);
5254 }
5255
5256 //----------------------------------
5257 // UNIT-ENABLED CMATH FUNCTIONS
5258 //----------------------------------
5259
5260 //----------------------------------
5261 // MIN/MAX FUNCTIONS
5262 //----------------------------------
5263
5264 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5266 constexpr auto min(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs)
5267 {
5268 // The result unit is the left operand's when that is lossless (matching operator+/-), falling back to the
5269 // common unit only when returning the left unit would truncate an integer -- never an anonymous common unit
5270 // for otherwise-representable operands. Computed in the body so the trait is not instantiated for a non-unit
5271 // the constraint already rejects. min/max select an operand, so the underlying is NOT floating-point promoted.
5272 using ResultUnit = detail::lhs_result_unit_t<UnitTypeLhs, UnitTypeRhs>;
5273 return (lhs < rhs ? ResultUnit(lhs) : ResultUnit(rhs));
5274 }
5275
5276 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5278 constexpr auto max(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs)
5280 using ResultUnit = detail::lhs_result_unit_t<UnitTypeLhs, UnitTypeRhs>;
5281 return (lhs > rhs ? ResultUnit(lhs) : ResultUnit(rhs));
5282 }
5283
5286 template<UnitType UnitTypeValue, UnitType UnitTypeLo, UnitType UnitTypeHi>
5287 requires(same_dimension<UnitTypeValue, UnitTypeLo> && same_dimension<UnitTypeValue, UnitTypeHi>)
5288 constexpr auto clamp(const UnitTypeValue& value, const UnitTypeLo& lo, const UnitTypeHi& hi)
5289 {
5290 // Reconcile to the common unit of all three operands for a correct comparison, then express the result in the
5291 // value's unit when lossless (as min/max do), never an anonymous unit for representable operands.
5292 using ResultUnit = detail::lhs_result_unit_t<UnitTypeValue, std::common_type_t<UnitTypeLo, UnitTypeHi>>;
5293 return (value < lo ? ResultUnit(lo) : (hi < value ? ResultUnit(hi) : ResultUnit(value)));
5294 }
5295
5296 //----------------------------------
5297 // TRANSCENDENTAL FUNCTIONS
5298 //----------------------------------
5299
5300 // it makes NO SENSE to put dimensioned units into a transcendental function, and if you think it does you are
5301 // demonstrably wrong. https://en.wikipedia.org/wiki/Transcendental_function#Dimensional_analysis
5302
5305 * @brief Compute exponential function
5306 * @details Returns the base-e exponential function of x, which is e raised to the power x: ex.
5307 * @param[in] x dimensionless value of the exponent.
5308 * @returns Exponential value of x.
5309 * If the magnitude of the result is too large to be represented by a value of the return type, the
5310 * function returns HUGE_VAL (or HUGE_VALF or HUGE_VALL) with the proper sign, and an overflow range
5311 * error occurs
5312 */
5313 template<DimensionlessUnitType UnitType>
5314 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> exp(const UnitType x) noexcept
5315 {
5316 return std::exp(x.value());
5317 }
5318
5320 * @ingroup UnitMath
5321 * @brief Compute natural logarithm
5322 * @details Returns the natural logarithm of x.
5323 * @param[in] x dimensionless value whose logarithm is calculated. If the argument is negative, a
5324 * domain error occurs.
5325 * @sa log10 for more common base-10 logarithms
5326 * @returns Natural logarithm of x.
5327 */
5328 template<DimensionlessUnitType UnitType>
5329 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> log(const UnitType x) noexcept
5330 {
5331 return std::log(x.value());
5332 }
5333
5334 /**
5335 * @ingroup UnitMath
5336 * @brief Compute common logarithm
5337 * @details Returns the common (base-10) logarithm of x.
5338 * @param[in] x Value whose logarithm is calculated. If the argument is negative, a
5339 * domain error occurs.
5340 * @returns Common logarithm of x.
5341 */
5342 template<DimensionlessUnitType UnitType>
5343 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> log10(const UnitType x) noexcept
5344 {
5345 return std::log10(x.value());
5346 }
5347
5351 * @details The integer part is stored in the object pointed by intpart, and the
5352 * fractional part is returned by the function. Both parts have the same sign
5353 * as x.
5354 * @param[in] x dimensionless value to break into parts.
5355 * @param[in] intpart Pointer to an object (of the same type as x) where the integral part
5356 * is stored with the same sign as x.
5357 * @returns The fractional part of x, with the same sign.
5358 */
5359 template<DimensionlessUnitType UnitType>
5360 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> modf(const UnitType x, UnitType* intpart) noexcept
5361 {
5362 using promoted = detail::floating_point_promotion_t<typename UnitType::underlying_type>;
5363 // std::modf splits the NORMALIZED value; the integral and fractional parts are already in the
5364 // quantity's own (normalized) units. Re-wrapping the fractional double through UnitType's
5365 // value constructor would re-apply the unit's scale (e.g. percent's 1/100) a second time, so the
5366 // fraction is returned as a plain dimensionless value and the integral part is converted back to
5367 // UnitType through its converting constructor.
5368 promoted intp;
5369 promoted fracpart = std::modf(x.template to<promoted>(), &intp);
5370 *intpart = dimensionless<promoted>{intp};
5371 return dimensionless<promoted>{fracpart};
5372 }
5377
5381 template<DimensionlessUnitType UnitType>
5382 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> exp2(const UnitType x) noexcept
5383 {
5384 return std::exp2(x.value());
5385 }
5386
5387 /**
5388 * @ingroup UnitMath
5389 * @brief Compute exponential minus one
5390 * @details Returns e raised to the power x minus one: e^x-1. For small magnitude values
5391 * of x, expm1 may be more accurate than exp(x)-1.
5392 * @param[in] x Value of the exponent.
5393 * @returns e raised to the power of x, minus one.
5394 */
5395 template<DimensionlessUnitType UnitType>
5396 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> expm1(const UnitType x) noexcept
5397 {
5398 return std::expm1(x.value());
5399 }
5400
5402 * @ingroup UnitMath
5403 * @brief Compute logarithm plus one
5404 * @details Returns the natural logarithm of one plus x. For small magnitude values of
5405 * x, logp1 may be more accurate than log(1+x).
5406 * @param[in] x Value whose logarithm is calculated. If the argument is less than -1, a
5407 * domain error occurs.
5408 * @returns The natural logarithm of (1+x).
5409 */
5410 template<DimensionlessUnitType UnitType>
5411 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> log1p(const UnitType x) noexcept
5412 {
5413 return std::log1p(x.value());
5414 }
5415
5416 /**
5417 * @ingroup UnitMath
5418 * @brief Compute binary logarithm
5419 * @details Returns the binary (base-2) logarithm of x.
5420 * @param[in] x Value whose logarithm is calculated. If the argument is negative, a
5421 * domain error occurs.
5422 * @returns The binary logarithm of x: log2x.
5423 */
5424 template<DimensionlessUnitType UnitType>
5425 constexpr dimensionless<detail::floating_point_promotion_t<typename UnitType::underlying_type>> log2(const UnitType x) noexcept
5426 {
5427 return std::log2(x.value());
5428 }
5429
5430 //----------------------------------
5431 // POWER FUNCTIONS
5432 //----------------------------------
5433
5434 /* pow is implemented earlier in the library since a lot of the unit definitions depend on it */
5435
5441 * @returns new unit, whose units are the square root of value's. E.g. if values
5442 * had units of `square_meter`, then the return type will have units of
5443 * `meter`.
5444 * @note `sqrt` provides a _rational approximation_ of the square root of <i>value</i>.
5445 * In some cases, _both_ the returned value _and_ conversion factor of the returned
5446 * unit type may have errors no larger than `1e-10`.
5447 */
5448 template<UnitType UnitType>
5449 requires(traits::has_linear_scale_v<UnitType>)
5450 constexpr auto sqrt(const UnitType& value) noexcept
5451 -> 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>>>
5452 {
5453 return decltype(units::sqrt(value))(sqrt(value.raw()));
5454 }
5455
5459 * @details Only implemented for linear_scale units.
5460 * @param[in] x unit type value
5461 * @param[in] y unit type value
5462 * @returns square root of the sum-of-squares of x and y, in x's unit when that is lossless (both operands
5463 * floating point, or y converts into x's unit without truncation), otherwise in the common unit of
5464 * x and y so no value is truncated.
5465 */
5466 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5467 requires(same_dimension<UnitTypeLhs, UnitTypeRhs> && traits::has_linear_scale_v<UnitTypeLhs, UnitTypeRhs>)
5468 constexpr auto hypot(const UnitTypeLhs& x, const UnitTypeRhs& y)
5469 {
5470 // The result unit is computed in the body (not the signature) so lhs_result_unit_t is never instantiated for
5471 // a non-unit operand that the constraint above already rejects -- a stricter compiler evaluates an explicit
5472 // return type during overload resolution and would otherwise hard-error on, e.g., fmod(double, double).
5473 using Result = detail::floating_point_promotion_t<detail::lhs_result_unit_t<UnitTypeLhs, UnitTypeRhs>>;
5474 return Result(std::hypot(Result(x).raw(), Result(y).raw()));
5475 }
5476
5477 //----------------------------------
5478 // ROUNDING FUNCTIONS
5479 //----------------------------------
5484
5488 template<UnitType Unit>
5489 constexpr detail::floating_point_promotion_t<Unit> ceil(const Unit x) noexcept
5490 {
5491 return detail::floating_point_promotion_t<Unit>(std::ceil(x.raw()));
5492 }
5497
5501 template<UnitType Unit>
5502 constexpr detail::floating_point_promotion_t<Unit> floor(const Unit x) noexcept
5503 {
5504 return detail::floating_point_promotion_t<Unit>(std::floor(x.raw()));
5505 }
5506
5509 * @brief Compute remainder of division
5510 * @details Returns the floating-point remainder of numer/denom (rounded towards zero).
5511 * @param[in] numer Value of the quotient numerator.
5512 * @param[in] denom Value of the quotient denominator.
5513 * @returns The remainder of dividing the arguments, in numer's unit when that is lossless, otherwise in the
5514 * common unit of the arguments.
5515 */
5516 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5518 constexpr auto fmod(const UnitTypeLhs numer, const UnitTypeRhs denom) noexcept
5519 {
5520 using Result = detail::floating_point_promotion_t<detail::lhs_result_unit_t<UnitTypeLhs, UnitTypeRhs>>;
5521 return Result(std::fmod(Result(numer).raw(), Result(denom).raw()));
5522 }
5523
5524 /**
5525 * @ingroup UnitMath
5526 * @brief Truncate value
5527 * @details Rounds x toward zero, returning the nearest integral value that is not
5528 * larger in magnitude than x. Effectively rounds towards 0.
5529 * @param[in] x Value to truncate
5530 * @returns The nearest integral value that is not larger in magnitude than x.
5531 */
5532 template<UnitType UnitType>
5533 constexpr detail::floating_point_promotion_t<UnitType> trunc(const UnitType x) noexcept
5534 {
5535 return detail::floating_point_promotion_t<UnitType>(std::trunc(x.raw()));
5536 }
5537
5538 /**
5539 * @ingroup UnitMath
5540 * @brief Round to nearest
5541 * @details Returns the integral value that is nearest to x, with halfway cases rounded
5542 * away from zero.
5543 * @param[in] x value to round.
5544 * @returns The value of x rounded to the nearest integral.
5545 */
5546 template<UnitType UnitType>
5547 constexpr detail::floating_point_promotion_t<UnitType> round(const UnitType x) noexcept
5548 {
5549 return detail::floating_point_promotion_t<UnitType>(std::round(x.raw()));
5550 }
5551 // DOXYGEN IGNORE
5553 namespace detail
5554 {
5556 enum class rounding_mode
5557 {
5558 toward_neg_infinity,
5559 toward_pos_infinity,
5560 nearest_half_away,
5561 toward_zero
5562 };
5563
5570 template<class Int>
5571 constexpr Int apply_integer_rounding(Int q, Int r, Int den, rounding_mode mode) noexcept
5572 {
5573 if (r == 0)
5574 return q; // exact — every mode agrees
5575 switch (mode)
5576 {
5577 case rounding_mode::toward_zero:
5578 return q; // integer division already truncated toward zero
5579 case rounding_mode::toward_neg_infinity:
5580 return r < 0 ? q - 1 : q; // a nonzero negative remainder means the true value is below q
5581 case rounding_mode::toward_pos_infinity:
5582 return r > 0 ? q + 1 : q; // a nonzero positive remainder means the true value is above q
5583 case rounding_mode::nearest_half_away:
5584 {
5585 // Halfway-away-from-zero: step away from zero when twice the remainder magnitude reaches den.
5586 const Int twiceRemainder = (r < 0 ? -r : r) * 2;
5587 if (twiceRemainder >= den)
5588 return r < 0 ? q - 1 : q + 1;
5589 return q;
5590 }
5591 }
5592 return q;
5593 }
5594
5603 template<class To, class From>
5604 constexpr To rounded_unit_cast(const From& x, rounding_mode mode) noexcept
5605 {
5606 using ToRep = typename To::underlying_type;
5607 using FromRep = typename From::underlying_type;
5608
5609 if constexpr (std::is_integral_v<FromRep>)
5610 {
5611 // Exact integer path: value (in From units) * num / den, rounded on the integer remainder. The
5612 // intermediate is the widest UNSIGNED integer when both source and target are unsigned, so an unsigned
5613 // magnitude wider than the signed intermediate can hold (e.g. a uint64 near its maximum on a platform
5614 // whose widest signed type is 64-bit) is not narrowed to a negative value before the conversion; any
5615 // signed operand keeps the signed intermediate so negative sources and toward-negative rounding stay
5616 // correct. A conversion ratio's num and den are always positive, so the quotient and remainder are
5617 // non-negative on the unsigned path and the rounding modes behave as for a non-negative value.
5618 using Ratio = std::ratio_divide<typename From::conversion_factor::conversion_ratio, typename To::conversion_factor::conversion_ratio>;
5619 using Intermediate = std::conditional_t<std::is_unsigned_v<FromRep> && std::is_unsigned_v<ToRep>, widest_unsigned_int, widest_signed_int>;
5620 const Intermediate value = static_cast<Intermediate>(x.raw());
5621 const Intermediate product = value * static_cast<Intermediate>(Ratio::num);
5622 const Intermediate den = static_cast<Intermediate>(Ratio::den);
5623 const Intermediate quotient = product / den;
5624 const Intermediate remainder = product % den;
5625 const Intermediate rounded = apply_integer_rounding(quotient, remainder, den, mode);
5626 return To(static_cast<ToRep>(rounded), linearized_value);
5627 }
5628 else
5629 {
5630 // A floating-point source: express in the target unit and apply the matching std:: rounding.
5631 using Promoted = unit<typename To::conversion_factor, floating_point_promotion_t<ToRep>, typename To::numerical_scale_type>;
5632 const auto inTarget = Promoted(x).to_linearized();
5633 const auto rounded = mode == rounding_mode::toward_neg_infinity ? std::floor(inTarget)
5634 : mode == rounding_mode::toward_pos_infinity ? std::ceil(inTarget)
5635 : mode == rounding_mode::nearest_half_away ? std::round(inTarget)
5636 : std::trunc(inTarget);
5637 return To(static_cast<ToRep>(rounded), linearized_value);
5638 }
5639 }
5640
5645 template<class To, class From>
5646 inline constexpr bool is_roundable_unit_conversion =
5647 traits::is_unit_v<To> && traits::is_unit_v<From> && same_dimension<From, To> &&
5648 std::is_integral_v<typename To::underlying_type> && !is_losslessly_convertible_unit<From, To>;
5649 } // namespace detail // END DOXYGEN IGNORE
5651
5657 * number of bytes), `units::floor<bytes<int>>(someRuntimeBits)` states the rounding intent and
5658 * yields the number of whole bytes at or below the value. Same shape as `std::chrono::floor<To>`.
5659 * @tparam To the coarser integral target unit (e.g. `bytes<int>`).
5660 * @tparam From the source unit (deduced), same dimension as `To`.
5661 * @param[in] x the value to convert.
5662 * @return `x` in units of `To`, rounded toward negative infinity.
5663 */
5664 template<class To, UnitType From>
5665 requires detail::is_roundable_unit_conversion<To, From>
5666 constexpr To floor(const From& x) noexcept
5667 {
5668 return detail::rounded_unit_cast<To>(x, detail::rounding_mode::toward_neg_infinity);
5669 }
5670
5673 * @brief Convert to a coarser integral unit, rounding up (toward positive infinity).
5674 * @details Run-time lossy conversion with explicit rounding intent; see `floor<To>`.
5675 * @tparam To the coarser integral target unit.
5676 * @tparam From the source unit (deduced), same dimension as `To`.
5677 * @param[in] x the value to convert.
5678 * @return `x` in units of `To`, rounded toward positive infinity.
5679 */
5680 template<class To, UnitType From>
5681 requires detail::is_roundable_unit_conversion<To, From>
5682 constexpr To ceil(const From& x) noexcept
5683 {
5684 return detail::rounded_unit_cast<To>(x, detail::rounding_mode::toward_pos_infinity);
5685 }
5686
5689 * @brief Convert to a coarser integral unit, rounding to nearest (halfway away from zero).
5690 * @details Run-time lossy conversion with explicit rounding intent; see `floor<To>`.
5691 * @tparam To the coarser integral target unit.
5692 * @tparam From the source unit (deduced), same dimension as `To`.
5693 * @param[in] x the value to convert.
5694 * @return `x` in units of `To`, rounded to the nearest whole target unit.
5695 */
5696 template<class To, UnitType From>
5697 requires detail::is_roundable_unit_conversion<To, From>
5698 constexpr To round(const From& x) noexcept
5699 {
5700 return detail::rounded_unit_cast<To>(x, detail::rounding_mode::nearest_half_away);
5701 }
5702
5705 * @brief Convert to a coarser integral unit, rounding toward zero.
5706 * @details Run-time lossy conversion with explicit rounding intent; see `floor<To>`.
5707 * @tparam To the coarser integral target unit.
5708 * @tparam From the source unit (deduced), same dimension as `To`.
5709 * @param[in] x the value to convert.
5710 * @return `x` in units of `To`, rounded toward zero.
5711 */
5712 template<class To, UnitType From>
5713 requires detail::is_roundable_unit_conversion<To, From>
5714 constexpr To trunc(const From& x) noexcept
5715 {
5716 return detail::rounded_unit_cast<To>(x, detail::rounding_mode::toward_zero);
5717 }
5718
5719 //----------------------------------
5720 // FLOATING POINT MANIPULATION
5721 //----------------------------------
5722
5724 * @ingroup UnitMath
5725 * @brief Copy sign
5726 * @details Returns a value with the magnitude and dimension of x, and the sign of y.
5727 * Values x and y do not have to be compatible units.
5728 * @param[in] x Value with the magnitude of the resulting value.
5729 * @param[in] y Value with the sign of the resulting value.
5730 * @returns value with the magnitude and dimension of x, and the sign of y.
5732 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5733 constexpr detail::floating_point_promotion_t<UnitTypeLhs> copysign(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
5734 {
5735 return detail::floating_point_promotion_t<UnitTypeLhs>(std::copysign(x.raw(), y.raw())); // no need for conversion to get the correct sign.
5736 }
5737
5739 template<UnitType UnitTypeLhs, ArithmeticType T>
5740 constexpr detail::floating_point_promotion_t<UnitTypeLhs> copysign(const UnitTypeLhs x, const T& y) noexcept
5741 {
5742 return detail::floating_point_promotion_t<UnitTypeLhs>(std::copysign(x.raw(), y));
5743 }
5744
5745 //----------------------------------
5746 // MIN / MAX / DIFFERENCE
5747 //----------------------------------
5748
5751 * @brief Positive difference
5752 * @details The function returns x-y if x>y, and zero otherwise, in x's unit when that is lossless, otherwise
5753 * in the common unit of x and y.
5754 * @param[in] x Values whose difference is calculated.
5755 * @param[in] y Values whose difference is calculated.
5756 * @returns The positive difference between x and y.
5757 */
5758 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5760 constexpr auto fdim(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
5761 {
5762 using Result = detail::floating_point_promotion_t<detail::lhs_result_unit_t<UnitTypeLhs, UnitTypeRhs>>;
5763 return Result(std::fdim(Result(x).raw(), Result(y).raw()));
5764 }
5765
5768 * @brief Maximum value
5769 * @details Returns the larger of its arguments: either x or y, in x's unit when that is lossless, otherwise
5770 * in the common unit of x and y.
5771 * @param[in] x Values among which the function selects a maximum.
5772 * @param[in] y Values among which the function selects a maximum.
5773 * @returns The maximum numeric value of its arguments.
5774 */
5775 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5777 constexpr auto fmax(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
5778 {
5779 using Result = detail::floating_point_promotion_t<detail::lhs_result_unit_t<UnitTypeLhs, UnitTypeRhs>>;
5780 return Result(std::fmax(Result(x).raw(), Result(y).raw()));
5781 }
5782
5785 * @brief Minimum value
5786 * @details Returns the smaller of its arguments: either x or y, in x's unit when that is lossless, otherwise
5787 * in the common unit of x and y. If one of the arguments in a NaN, the other is returned.
5788 * @param[in] x Values among which the function selects a minimum.
5789 * @param[in] y Values among which the function selects a minimum.
5790 * @returns The minimum numeric value of its arguments.
5791 */
5792 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5794 constexpr auto fmin(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
5795 {
5796 using Result = detail::floating_point_promotion_t<detail::lhs_result_unit_t<UnitTypeLhs, UnitTypeRhs>>;
5797 return Result(std::fmin(Result(x).raw(), Result(y).raw()));
5798 }
5799
5800 //----------------------------------
5801 // OTHER FUNCTIONS
5802 //----------------------------------
5807
5811 template<UnitType UnitType>
5812 constexpr detail::floating_point_promotion_t<UnitType> fabs(const UnitType x) noexcept
5813 {
5814 return detail::floating_point_promotion_t<UnitType>(std::fabs(x.raw()));
5815 }
5820
5824 template<UnitType UnitType>
5825 constexpr UnitType abs(const UnitType x) noexcept
5826 {
5827 return UnitType(std::abs(x.raw()));
5828 }
5829
5839 * @param[in] x Value to be multiplied.
5840 * @param[in] y Value to be multiplied.
5841 * @param[in] z Value to be added.
5842 * @returns The result of x*y+z.
5843 */
5844 template<UnitType UnitTypeLhs, UnitType UnitMultiply, UnitType UnitAdd>
5845 requires(traits::is_same_dimension_conversion_factor_v<
5846 compound_conversion_factor<typename traits::unit_traits<UnitTypeLhs>::conversion_factor, typename traits::unit_traits<UnitMultiply>::conversion_factor>,
5847 typename traits::unit_traits<UnitAdd>::conversion_factor>)
5848 constexpr auto fma(const UnitTypeLhs x, const UnitMultiply y, const UnitAdd z) noexcept
5849 -> std::common_type_t<decltype(detail::floating_point_promotion_t<UnitTypeLhs>(x) * detail::floating_point_promotion_t<UnitMultiply>(y)), UnitAdd>
5850 {
5851 using CommonUnit = decltype(units::fma(x, y, z));
5852 using ProductUnit = decltype(detail::floating_point_promotion_t<UnitTypeLhs>(x) * detail::floating_point_promotion_t<UnitMultiply>(y));
5853
5854 // Fold the product-unit -> result-unit conversion into one multiplicand (a compile-time-constant
5855 // scale), so a SINGLE std::fma performs the multiply and the add in the result's basis with one
5856 // rounding: x_raw * (y_raw * scale) + z_in_result. Feeding the raw operands directly (each in its
5857 // own unit) would combine inconsistent bases and give a wrong result.
5858 constexpr auto scale = CommonUnit(ProductUnit(1)).raw();
5859 return CommonUnit(std::fma(x.raw(), y.raw() * scale, CommonUnit(z).raw()));
5860 }
5861
5862 //----------------------------
5863 // NAN support
5864 //----------------------------
5865
5866 template<UnitType UnitType>
5867 constexpr bool isnan(const UnitType& x) noexcept
5868 {
5869 return std::isnan(x.raw());
5870 }
5871
5872 template<UnitType UnitType>
5873 constexpr bool isinf(const UnitType& x) noexcept
5874 {
5875 return std::isinf(x.raw());
5876 }
5877
5878 template<UnitType UnitType>
5879 constexpr bool isfinite(const UnitType& x) noexcept
5880 {
5881 return std::isfinite(x.raw());
5882 }
5883
5884 template<UnitType UnitType>
5885 constexpr bool isnormal(const UnitType& x) noexcept
5886 {
5887 return std::isnormal(x.raw());
5888 }
5889
5890 template<UnitType UnitTypeLhs, UnitType UnitTypeRhs>
5892 constexpr bool isunordered(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept
5893 {
5894 return std::isunordered(lhs.raw(), rhs.raw());
5895 }
5896} // end namespace units
5897
5898//----------------------------------------------------------------------------------------------------------------------
5899// STD Namespace extensions
5900//----------------------------------------------------------------------------------------------------------------------
5901
5902//------------------------------
5903// std::hash
5904//------------------------------
5905
5906template<class ConversionFactor, typename T, class NumericalScale>
5907struct std::hash<units::unit<ConversionFactor, T, NumericalScale>>
5908{
5909 template<typename U = T>
5910 constexpr std::size_t operator()(const units::unit<ConversionFactor, T, NumericalScale>& x) const noexcept
5911 {
5912 if constexpr (std::is_integral_v<U>)
5913 {
5914 return static_cast<std::size_t>(x.to_linearized());
5915 }
5916 else
5917 {
5918 return static_cast<std::size_t>(hash<T>()(x.to_linearized()));
5920 }
5921};
5922
5923// A NAMED unit is a class deriving from unit<...>; the exact-pattern specialization above does not match it, so its
5924// std::hash falls to the deleted primary. Inherit the base unit's hash (it operates on the linearized value, which the
5925// named unit has via its base) so a named unit is hashable exactly like the plain unit<...> it represents.
5926template<class Named>
5927 requires units::detail::is_named_unit_v<Named>
5928struct std::hash<Named> : std::hash<units::detail::unit_base_t<Named>>
5929{
5931
5932//----------------------------------------------------------------------------------------------------------------------
5933// NUMERIC LIMITS
5934//----------------------------------------------------------------------------------------------------------------------
5935
5936namespace std
5937{
5938 template<units::ConversionFactorType ConversionFactor, units::ArithmeticType T, units::NumericalScaleType<T> NonLinearScale>
5939 struct numeric_limits<units::unit<ConversionFactor, T, NonLinearScale>>
5940 {
5942 {
5943 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::min());
5944 }
5945
5946 static constexpr units::unit<ConversionFactor, T, NonLinearScale> denorm_min() noexcept
5947 {
5948 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::denorm_min());
5949 }
5950
5952 {
5953 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::max());
5954 }
5955
5956 static constexpr units::unit<ConversionFactor, T, NonLinearScale> lowest()
5957 {
5958 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::lowest());
5959 }
5960
5961 static constexpr units::unit<ConversionFactor, T, NonLinearScale> epsilon()
5962 {
5963 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::epsilon());
5964 }
5965
5966 static constexpr units::unit<ConversionFactor, T, NonLinearScale> round_error()
5967 {
5968 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::round_error());
5969 }
5970
5971 static constexpr units::unit<ConversionFactor, T, NonLinearScale> infinity()
5972 {
5973 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::infinity());
5974 }
5975
5976 static constexpr units::unit<ConversionFactor, T, NonLinearScale> quiet_NaN()
5977 {
5978 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::quiet_NaN());
5979 }
5980
5981 static constexpr units::unit<ConversionFactor, T, NonLinearScale> signaling_NaN()
5982 {
5983 return units::unit<ConversionFactor, T, NonLinearScale>(std::numeric_limits<T>::signaling_NaN());
5984 }
5985
5986 static constexpr bool is_specialized = std::numeric_limits<T>::is_specialized;
5987 static constexpr bool is_signed = std::numeric_limits<T>::is_signed;
5988 static constexpr bool is_integer = std::numeric_limits<T>::is_integer;
5989 static constexpr bool is_exact = std::numeric_limits<T>::is_exact;
5990 static constexpr bool has_infinity = std::numeric_limits<T>::has_infinity;
5991 static constexpr bool has_quiet_NaN = std::numeric_limits<T>::has_quiet_NaN;
5992 static constexpr bool has_signaling_NaN = std::numeric_limits<T>::has_signaling_NaN;
5993 };
5994
5995 // A NAMED unit is a class deriving from unit<...>; the exact-pattern specialization above does not match it.
5996 // Return the NAMED type from each limit (the named unit converts from its base), so both the VALUE and the
5997 // reported TYPE match the named unit — generic code that asks for numeric_limits<meters<double>>::max() gets a
5998 // meters<double> back, not the plain unit<...> base.
5999 template<class Named>
6000 requires units::detail::is_named_unit_v<Named>
6001 struct numeric_limits<Named> : numeric_limits<units::detail::unit_base_t<Named>>
6002 {
6003 private:
6004 using Base = numeric_limits<units::detail::unit_base_t<Named>>;
6005
6006 public:
6007 // Inherit every flag/member from the base (has_infinity, is_signed, digits, ...); only SHADOW the
6008 // value-returning statics to return the NAMED type (the named unit converts from its base), so both the value
6009 // and the reported type match the named unit.
6010 static constexpr Named min() { return Named(Base::min()); }
6011 static constexpr Named max() { return Named(Base::max()); }
6012 static constexpr Named lowest() { return Named(Base::lowest()); }
6013 static constexpr Named epsilon() { return Named(Base::epsilon()); }
6014 static constexpr Named round_error() { return Named(Base::round_error()); }
6015 static constexpr Named denorm_min() { return Named(Base::denorm_min()); }
6016 static constexpr Named infinity() { return Named(Base::infinity()); }
6017 static constexpr Named quiet_NaN() { return Named(Base::quiet_NaN()); }
6018 static constexpr Named signaling_NaN() { return Named(Base::signaling_NaN()); }
6019 };
6020
6021 // These overloads accept ANY unit — including a NAMED unit, which is a class DERIVING from units::unit<...>.
6022 // Constraining on the units::UnitType concept (rather than an exact `unit<Cf,T,Ns>&` parameter) makes a named
6023 // unit an EXACT match, so it wins over <cmath>'s own generic isnan/isinf/... templates. With the exact-type
6024 // parameter, a named (derived) unit only bound via a derived->base conversion — a WORSE match than <cmath>'s
6025 // template — so on some standard libraries (MSVC) the generic <cmath> overload was selected and forwarded the
6026 // unit to fpclassify(), which has no unit overload (error C2665). raw() yields the arithmetic magnitude.
6027 template<units::UnitType U>
6028 constexpr bool isnan(U x)
6029 {
6030 return std::isnan(x.raw());
6031 }
6032
6033 template<units::UnitType U>
6034 constexpr bool isinf(U x)
6035 {
6036 return std::isinf(x.raw());
6037 }
6038
6039 template<units::UnitType U>
6040 constexpr bool isfinite(U x)
6041 {
6042 return std::isfinite(x.raw());
6043 }
6044
6045 template<units::UnitType U>
6046 constexpr bool signbit(U x)
6047 {
6048 return std::signbit(x.raw());
6049 }
6050} // namespace std
6051
6052//------------------------------
6053// UNIT DEDUCTION GUIDES
6054//------------------------------
6055
6056namespace units
6057{
6058 // Concept to ensure we only apply the dimensionless fallback
6059 // to a pure, unmodified dimensionless unit.
6060 template<class Cf>
6061 concept PureDimensionlessCF = std::is_same_v<typename Cf::dimension_type, dimension::dimensionless> && std::ratio_equal_v<typename Cf::conversion_ratio, std::ratio<1>> &&
6062 std::ratio_equal_v<typename Cf::pi_exponent_ratio, std::ratio<0>> && std::ratio_equal_v<typename Cf::translation_ratio, std::ratio<0>>;
6063
6064 // 1) chrono deduction guide
6065 template<ArithmeticType Rep, RatioType Period>
6066 unit(std::chrono::duration<Rep, Period>) -> unit<conversion_factor<Period, dimension::time>, Rep>;
6067
6068 // 2) Dimensionless fallback:
6069 // Only applies if the source is exactly the base dimensionless unit.
6070 template<ArithmeticType SourceTy, ConversionFactorType SourceCf>
6071 requires(traits::is_unit_v<unit<SourceCf, SourceTy>> && PureDimensionlessCF<SourceCf>)
6072 unit(const unit<SourceCf, SourceTy>&) -> unit<conversion_factor<std::ratio<1>, dimension::dimensionless>, SourceTy>;
6073
6074 // 3) Lossless integral conversion:
6075 // For dimensionally compatible units where the conversion is integral and lossless.
6076 // This applies only if is_losslessly_convertible_unit is true.
6077 template<ArithmeticType SourceTy, ConversionFactorType SourceCf, ConversionFactorType TargetCf = SourceCf>
6078 requires(traits::is_unit_v<unit<SourceCf, SourceTy>> && traits::is_conversion_factor_v<TargetCf> && traits::is_same_dimension_conversion_factor_v<SourceCf, TargetCf> &&
6079 !std::is_same_v<SourceCf, TargetCf> && detail::is_losslessly_convertible_unit<unit<SourceCf, SourceTy>, unit<TargetCf, SourceTy>>)
6081
6082 // 4) Non-lossless conversions:
6083 // For dimensionally compatible units where integral conversion is not possible.
6084 // Falls back to floating point.
6085 template<ArithmeticType SourceTy, ConversionFactorType SourceCf, ConversionFactorType TargetCf = SourceCf>
6086 requires(traits::is_unit_v<unit<SourceCf, SourceTy>> && traits::is_conversion_factor_v<TargetCf> && traits::is_same_dimension_conversion_factor_v<SourceCf, TargetCf> &&
6087 !std::is_same_v<SourceCf, TargetCf> && !detail::is_losslessly_convertible_unit<unit<SourceCf, SourceTy>, unit<TargetCf, SourceTy>>)
6089
6090 // 5) Exact matches:
6091 // If the unit already matches `unit<TargetCf, SourceTy>`, use it directly.
6092 template<ConversionFactorType TargetCf, ArithmeticType SourceTy>
6093 requires traits::is_unit_v<unit<TargetCf, SourceTy>>
6095
6096 // 6) Deduce type from arithmetic type (dimensionless by default)
6097 template<typename T, typename Cf = dimension::dimensionless, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
6098 unit(T) -> unit<Cf, T>;
6099} // namespace units
6100
6101//----------------------------------------------------------------------------------------------------------------------
6102// std::format SUPPORT
6103//----------------------------------------------------------------------------------------------------------------------
6104
6105#if defined(UNIT_LIB_ENABLE_FORMAT)
6106
6107//----------------------------------------------------------------------------------------------------------------------
6108// CLASS: std::formatter<units::unit<...>, char>
6109//----------------------------------------------------------------------------------------------------------------------
6126//----------------------------------------------------------------------------------------------------------------------
6127template<units::UnitType U>
6128struct std::formatter<U, char>
6129{
6130 using conversion_factor = typename units::traits::unit_traits<U>::conversion_factor;
6131 using value_type = typename units::traits::unit_traits<U>::underlying_type;
6132 using scale_type = typename units::traits::unit_traits<U>::numerical_scale_type;
6133 using promoted_value_type = units::detail::floating_point_promotion_t<value_type>;
6134
6135 // A named unit prints its stored value as-is, so its value formatter is the underlying type — integer
6136 // specs (d/x/b/…) then work for an integer-underlying unit. An unnamed unit is rendered in its base
6137 // unit, a conversion that is floating-point, so its value formatter is the promoted type.
6138 static constexpr bool renders_in_base_unit = units::detail::label_uses_base_unit<conversion_factor, value_type, scale_type>();
6139 using formatted_value_type = std::conditional_t<renders_in_base_unit, promoted_value_type, value_type>;
6140
6141 // The %b flag base-converts a NAMED unit's value to SI, which is a floating-point result; it is emitted
6142 // through a promoted-type formatter. (For an unnamed unit the primary formatter is already promoted.)
6143 std::formatter<formatted_value_type, char> m_valueFormatter;
6144 std::formatter<promoted_value_type, char> m_baseFormatter;
6145 units::detail::unit_format_options m_options;
6146 bool m_usesBaseFormatter = false;
6147
6148 //----------------------------------------------------------------------------------------------------------------------
6149 // FUNCTION: parse [public]
6150 //----------------------------------------------------------------------------------------------------------------------
6155 //----------------------------------------------------------------------------------------------------------------------
6156 constexpr auto parse(std::format_parse_context& ctx)
6157 {
6158 auto it = ctx.begin();
6159 auto end = ctx.end();
6160
6161 // The value-spec runs to the first '%' (or to the closing '}').
6162 auto valueSpecEnd = it;
6163 for (auto scan = it; scan != end && *scan != '}'; ++scan)
6164 {
6165 if (*scan == '%')
6166 break;
6167 valueSpecEnd = scan + 1;
6168 }
6169
6170 // The %b flag (base-SI conversion) needs the promoted-type formatter; every other flag uses the
6171 // stored-type formatter. Determine which is in play by scanning the unit-opts for 'b' before
6172 // delegating the value-spec, so the value-spec is parsed into exactly the formatter that will emit
6173 // it (parsing an integer spec such as `d` into a floating-point formatter would wrongly reject it).
6174 // The scan skips over quoted separator literals (`'…'`, with `\` escapes) exactly as the unit-opts
6175 // parser below does, so a 'b' inside a separator (e.g. `%'_b_'`) is separator text, not the base flag.
6176 m_usesBaseFormatter = false;
6177 for (auto scan = valueSpecEnd; scan != end && *scan != '}';)
6178 {
6179 if (*scan == '\'')
6180 {
6181 for (++scan; scan != end && *scan != '}' && *scan != '\''; ++scan)
6182 {
6183 if (*scan == '\\' && scan + 1 != end && *(scan + 1) != '}')
6184 ++scan; // skip the escaped character so an escaped quote does not end the literal
6185 }
6186 if (scan != end && *scan == '\'')
6187 ++scan; // consume the closing quote
6188 }
6189 else if (*scan == 'b')
6190 {
6191 m_usesBaseFormatter = true;
6192 break;
6193 }
6194 else
6195 {
6196 ++scan;
6197 }
6198 }
6199
6200 // Delegate the value-spec to the chosen value formatter. Present it a parse context spanning only
6201 // the value-spec and require it consumed the whole thing.
6202 if (valueSpecEnd != it)
6203 {
6204 std::string_view valueSpec(it, valueSpecEnd);
6205 if (m_usesBaseFormatter)
6206 {
6207 std::format_parse_context baseCtx(valueSpec);
6208 if (m_baseFormatter.parse(baseCtx) != valueSpec.end())
6209 throw std::format_error("units: invalid value format-spec");
6210 }
6211 else
6212 {
6213 std::format_parse_context valueCtx(valueSpec);
6214 if (m_valueFormatter.parse(valueCtx) != valueSpec.end())
6215 throw std::format_error("units: invalid value format-spec");
6216 }
6217 }
6218
6219 it = valueSpecEnd;
6220
6221 // Unit-opts after '%'.
6222 if (it != end && *it == '%')
6223 {
6224 ++it;
6225 bool sawForm = false;
6226 bool sawShow = false;
6227 while (it != end && *it != '}')
6228 {
6229 const char c = *it;
6230 if (c == 'a' || c == 'n' || c == 'b')
6231 {
6232 if (sawForm)
6233 throw std::format_error("units: duplicate label-form flag");
6234 sawForm = true;
6235 m_options.form = (c == 'a') ? units::detail::label_form::abbreviation
6236 : (c == 'n') ? units::detail::label_form::name
6237 : units::detail::label_form::base;
6238 ++it;
6239 }
6240 else if (c == 'v' || c == 'u')
6241 {
6242 if (sawShow)
6243 throw std::format_error("units: duplicate show flag");
6244 sawShow = true;
6245 m_options.showValue = (c == 'v');
6246 m_options.showUnit = (c == 'u');
6247 ++it;
6248 }
6249 else if (c == '\'')
6250 {
6251 ++it; // opening quote
6252 std::string sep;
6253 bool closed = false;
6254 while (it != end && *it != '}')
6255 {
6256 if (*it == '\\')
6257 {
6258 ++it;
6259 if (it == end || *it == '}')
6260 throw std::format_error("units: dangling escape in separator");
6261 switch (*it)
6262 {
6263 case 't': sep.push_back('\t'); break;
6264 case 'n': sep.push_back('\n'); break;
6265 case '\\': sep.push_back('\\'); break;
6266 case '\'': sep.push_back('\''); break;
6267 default: sep.push_back(*it); break;
6268 }
6269 ++it;
6270 }
6271 else if (*it == '\'')
6272 {
6273 closed = true;
6274 ++it; // closing quote
6275 break;
6276 }
6277 else
6278 {
6279 sep.push_back(*it);
6280 ++it;
6281 }
6282 }
6283 if (!closed)
6284 throw std::format_error("units: unterminated separator literal");
6285 m_options.separator = std::move(sep);
6286 m_options.customSep = true;
6287 }
6288 else
6289 {
6290 throw std::format_error("units: unknown unit-format flag");
6291 }
6292 }
6293 }
6294
6295 return it;
6296 }
6297
6298 //----------------------------------------------------------------------------------------------------------------------
6299 // FUNCTION: format [public]
6300 //----------------------------------------------------------------------------------------------------------------------
6306 //----------------------------------------------------------------------------------------------------------------------
6307 template<class FormatContext>
6308 auto format(const U& obj, FormatContext& ctx) const
6309 {
6310 using base_unit_type = units::unit<units::conversion_factor<std::ratio<1>, typename conversion_factor::dimension_type>, promoted_value_type, scale_type>;
6311
6312 // The value: an unnamed unit is always rendered in its base unit (its honest label is the
6313 // base-dimension list); the %b flag likewise base-converts a named unit's value so the base-SI
6314 // label is honest. Otherwise a named unit shows its stored value as-is (so integer specs work).
6315 formatted_value_type value{};
6316 promoted_value_type baseValue{};
6317 if constexpr (renders_in_base_unit)
6318 value = base_unit_type(obj).raw();
6319 else
6320 value = static_cast<formatted_value_type>(obj.raw());
6321 if (m_options.form == units::detail::label_form::base)
6322 baseValue = base_unit_type(obj).raw();
6323
6324 std::string label;
6325 if (m_options.showUnit)
6326 {
6327 switch (m_options.form)
6328 {
6329 case units::detail::label_form::name: label = units::detail::unit_label<units::detail::label_form::name>(obj); break;
6330 case units::detail::label_form::base: label = units::detail::unit_label<units::detail::label_form::base>(obj); break;
6331 case units::detail::label_form::abbreviation:
6332 default: label = units::detail::unit_label<units::detail::label_form::abbreviation>(obj); break;
6333 }
6334 }
6335
6336 auto out = ctx.out();
6337
6338 if (m_options.showValue)
6339 {
6340 if (m_usesBaseFormatter)
6341 {
6342 // %b: emit the base-SI value through the promoted-type formatter. For an unnamed unit the
6343 // value is already the promoted base value; for a named unit it is the base-converted one.
6344 const promoted_value_type emitted = renders_in_base_unit ? static_cast<promoted_value_type>(value) : baseValue;
6345 out = m_baseFormatter.format(emitted, ctx);
6346 }
6347 else
6348 {
6349 out = m_valueFormatter.format(value, ctx);
6350 }
6351 }
6352
6353 if (m_options.showUnit && !label.empty())
6354 {
6355 // The core builders prefix a label with a single space (the default separator). Keep it when no
6356 // separator was overridden and a value precedes the label; otherwise strip it and, for a shown
6357 // value, emit the chosen separator.
6358 std::string_view labelView(label);
6359 const bool hasLeadingSpace = !labelView.empty() && labelView.front() == ' ';
6360
6361 if (m_options.showValue)
6362 {
6363 if (m_options.customSep)
6364 {
6365 if (hasLeadingSpace)
6366 labelView.remove_prefix(1);
6367 for (char ch : m_options.separator)
6368 *out++ = ch;
6369 }
6370 }
6371 else
6372 {
6373 if (hasLeadingSpace)
6374 labelView.remove_prefix(1);
6375 }
6376
6377 for (char ch : labelView)
6378 *out++ = ch;
6379 }
6380
6381 return out;
6382 }
6383};
6384
6385#endif // UNIT_LIB_ENABLE_FORMAT
6386
6387//----------------------------------------------------------------------------------------------------------------------
6388// JSON SUPPORT
6389//----------------------------------------------------------------------------------------------------------------------
6390
6391#if defined __has_include
6392#if __has_include(<nlohmann/json.hpp>)
6393#include <nlohmann/json.hpp>
6394namespace units
6395{
6396 template<class UnitType>
6397 requires(units::traits::is_unit_v<UnitType>)
6398 void from_json(const nlohmann::json& j, UnitType& u)
6399 {
6400 using underlying = typename units::traits::unit_traits<UnitType>::underlying_type;
6401 underlying value;
6402 j.get_to(value);
6403 u = UnitType(value);
6404 }
6405
6406 template<class UnitType>
6407 requires(units::traits::is_unit_v<UnitType>)
6408 void to_json(nlohmann::json& j, const UnitType& u)
6409 {
6410 j = u.raw();
6411 }
6412} // namespace units
6413#endif
6414#endif
6415
6416#endif // UNIT_CORE_H
Definition core.h:2741
ConversionFactor conversion_factor
Definition core.h:2746
constexpr auto value() const noexcept
Definition core.h:2995
constexpr T to_linearized() const noexcept
linearized unit value
Definition core.h:3052
T value_type
Definition core.h:2745
constexpr unit(const unit< ConversionFactorRhs, Ty, NsRhs > &rhs) noexcept
converting constructor
Definition core.h:2767
constexpr unit< Cf, Ty > convert() const noexcept
Definition core.h:3068
constexpr bool operator!=(const unit< ConversionFactorRhs, Ty, NsRhs > &rhs) const noexcept
Definition core.h:2972
constexpr underlying_type raw() const noexcept
Definition core.h:2983
T _linearized_value
Definition core.h:3214
constexpr unit & operator=(const underlying_type &rhs) noexcept
assignment
Definition core.h:2881
constexpr unit & operator=(const unit &rhs) noexcept=default
constexpr Ty to() const noexcept
Definition core.h:3027
T underlying_type
Definition core.h:2744
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:3900
Concept for types which represent units without a dimension (dimensionless).
Definition core.h:1036
Definition core.h:1539
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:6052
Definition core.h:1533
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:3451
@ base
the SI base-dimension list (" m s^-1"); pairs with a base-converted value.
Definition core.h:3454
@ name
the unit's own full name ("meters", "feet"); base-dimension list if unnamed.
Definition core.h:3453
@ abbreviation
the unit's own abbreviation ("m", "ft"), the default; base-dimension list if unnamed.
Definition core.h:3452
constexpr bool label_uses_base_unit()
Whether a unit's label is its dimension list rather than a named abbreviation.
Definition core.h:3504
std::string dimension_to_string(const dim< D, E > &)
Renders a single dimension term (base dimension + exponent) as text.
Definition core.h:3394
#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:3229
typename detail::prefix< std::ratio< 1152921504606846976 >, Cf >::type exbi
< Represents the type of class Cf with the binary 'pebi' prefix appended.
Definition core.h:2026
typename detail::prefix< std::ratio< 1125899906842624 >, Cf >::type pebi
< Represents the type of class Cf with the binary 'tebi' prefix appended.
Definition core.h:2025
typename detail::prefix< std::ratio< 1073741824 >, Cf >::type gibi
< Represents the type of class Cf with the binary 'mibi' prefix appended.
Definition core.h:2023
typename detail::prefix< std::ratio< 1048576 >, Cf >::type mebi
< Represents the type of class Cf with the binary 'kibi' prefix appended.
Definition core.h:2022
typename detail::prefix< std::ratio< 1099511627776 >, Cf >::type tebi
< Represents the type of class Cf with the binary 'gibi' prefix appended.
Definition core.h:2024
typename detail::prefix< std::ratio< 1024 >, Cf >::type kibi
< Represents the type of class Cf with the metric 'exa' prefix appended.
Definition core.h:2021
constexpr unit()=default
< Type of conversion_factor the unit represents (e.g. meters)
typename detail::compound_impl< Cf, Cfs... >::type compound_conversion_factor
Represents a conversion factor made up from other conversion factors.
Definition core.h:1959
constexpr T unit_cast(const Unit &value) noexcept
Casts an unit to an arithmetic type.
Definition core.h:3789
constexpr To convert(const From &value) noexcept
converts a value from an unit to another.
Definition core.h:2351
typename detail::prefix< std::deci, Cf >::type deci
< Represents the type of class Cf with the metric 'centi' prefix appended.
Definition core.h:2006
typename detail::prefix< std::centi, Cf >::type centi
< Represents the type of class Cf with the metric 'milli' prefix appended.
Definition core.h:2005
typename detail::prefix< std::giga, Cf >::type giga
< Represents the type of class Cf with the metric 'mega' prefix appended.
Definition core.h:2011
typename detail::prefix< std::micro, Cf >::type micro
< Represents the type of class Cf with the metric 'nano' prefix appended.
Definition core.h:2003
typename detail::prefix< std::mega, Cf >::type mega
< Represents the type of class Cf with the metric 'kilo' prefix appended.
Definition core.h:2010
typename detail::prefix< std::exa, Cf >::type exa
< Represents the type of class Cf with the metric 'peta' prefix appended.
Definition core.h:2014
typename detail::prefix< std::femto, Cf >::type femto
< Represents the type of class Cf with the metric 'atto' prefix appended.
Definition core.h:2000
typename detail::prefix< std::pico, Cf >::type pico
< Represents the type of class Cf with the metric 'femto' prefix appended.
Definition core.h:2001
typename detail::prefix< std::kilo, Cf >::type kilo
< Represents the type of class Cf with the metric 'hecto' prefix appended.
Definition core.h:2009
typename detail::prefix< std::milli, Cf >::type milli
< Represents the type of class Cf with the metric 'micro' prefix appended.
Definition core.h:2004
typename detail::prefix< std::peta, Cf >::type peta
< Represents the type of class Cf with the metric 'tera' prefix appended.
Definition core.h:2013
typename detail::prefix< std::nano, Cf >::type nano
< Represents the type of class Cf with the metric 'pico' prefix appended.
Definition core.h:2002
typename detail::prefix< std::tera, Cf >::type tera
< Represents the type of class Cf with the metric 'giga' prefix appended.
Definition core.h:2012
typename detail::prefix< std::deca, Cf >::type deca
< Represents the type of class Cf with the metric 'deci' prefix appended.
Definition core.h:2007
typename detail::prefix< std::hecto, Cf >::type hecto
< Represents the type of class Cf with the metric 'deca' prefix appended.
Definition core.h:2008
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:1875
typename detail::sqrt_impl< Cf, Eps >::type square_root
represents the square root of type class U.
Definition core.h:1917
typename detail::cubed_impl< Cf >::type cubed
represents the type of class U cubed.
Definition core.h:1701
typename detail::squared_impl< Cf >::type squared
represents the unit type of class U squared
Definition core.h:1674
typename detail::inverse_impl< Cf >::type inverse
represents the inverse unit type of class U.
Definition core.h:1646
constexpr detail::floating_point_promotion_t< UnitType > trunc(const UnitType x) noexcept
Truncate value.
Definition core.h:5524
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:5351
constexpr auto fmod(const UnitTypeLhs numer, const UnitTypeRhs denom) noexcept
Compute remainder of division.
Definition core.h:5509
constexpr auto hypot(const UnitTypeLhs &x, const UnitTypeRhs &y)
Computes the square root of the sum-of-squares of x and y.
Definition core.h:5459
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > log1p(const UnitType x) noexcept
Compute logarithm plus one.
Definition core.h:5402
constexpr detail::floating_point_promotion_t< UnitType > fabs(const UnitType x) noexcept
Compute absolute value.
Definition core.h:5803
constexpr auto fmin(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
Minimum value.
Definition core.h:5785
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > log2(const UnitType x) noexcept
Compute binary logarithm.
Definition core.h:5416
constexpr auto fmax(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
Maximum value.
Definition core.h:5768
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:5839
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > exp(const UnitType x) noexcept
Compute exponential function.
Definition core.h:5305
constexpr detail::floating_point_promotion_t< UnitType > round(const UnitType x) noexcept
Round to nearest.
Definition core.h:5538
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > exp2(const UnitType x) noexcept
Compute binary exponential function.
Definition core.h:5373
constexpr detail::floating_point_promotion_t< Unit > ceil(const Unit x) noexcept
Round up value.
Definition core.h:5480
constexpr detail::floating_point_promotion_t< UnitTypeLhs > copysign(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
Copy sign.
Definition core.h:5724
constexpr detail::floating_point_promotion_t< Unit > floor(const Unit x) noexcept
Round down value.
Definition core.h:5493
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > log(const UnitType x) noexcept
Compute natural logarithm.
Definition core.h:5320
constexpr auto fdim(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
Positive difference.
Definition core.h:5751
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > log10(const UnitType x) noexcept
Compute common logarithm.
Definition core.h:5334
constexpr dimensionless< detail::floating_point_promotion_t< typename UnitType::underlying_type > > expm1(const UnitType x) noexcept
Compute exponential minus one.
Definition core.h:5387
#define MSVC_EBO
Describes objects that represent quantities of a given unit.
Definition core.h:2737
constexpr UnitType make_unit(const T value) noexcept
Constructs a unit container from an arithmetic type.
Definition core.h:3370
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:1361
dimension_multiply< pressure, time > dynamic_viscosity
< Represents an SI derived unit of density
Definition core.h:1377
dimension_divide< mass, volume > density
< Represents an SI derived unit of torque
Definition core.h:1376
make_dimension< luminous_intensity, std::ratio< 1 >, length, std::ratio<-2 > > luminance
< Represents an SI derived unit of illuminance
Definition core.h:1360
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:1355
make_dimension< power, std::ratio< 1 >, length, std::ratio<-1 > > spectral_flux
< Represents an SI derived unit of spectral intensity
Definition core.h:1369
dimension_divide< mass, substance > substance_mass
< Represents an SI derived unit of radioactivity
Definition core.h:1362
make_dimension< radiant_intensity, std::ratio< 1 >, area, std::ratio<-1 > > radiance
< Represents an SI derived unit of radiant intensity
Definition core.h:1366
dimension_divide< voltage, current > impedance
< Represents an SI derived unit of capacitance
Definition core.h:1354
dimension_divide< substance, mass > substance_concentration
< Represents an SI derived unit of substance mass
Definition core.h:1363
dimension_divide< energy, time > power
< Represents an SI derived unit of energy
Definition core.h:1351
dimension_multiply< impedance, time > inductance
< Represents an SI derived unit of magnetic flux
Definition core.h:1357
dimension_pow< length, std::ratio< 3 > > volume
< Represents an SI derived unit of area
Definition core.h:1346
dimension_divide< area, time > kinematic_viscosity
< Represents an SI derived unit of dynamic (absolute) viscosity
Definition core.h:1378
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 angular jerk
Definition core.h:1344
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:1365
dimension_divide< angular_velocity, time > angular_acceleration
< Represents an SI derived unit of acceleration
Definition core.h:1342
dimension_divide< force, area > pressure
< Represents an SI derived unit of volumetric flow rate
Definition core.h:1348
make_dimension< radiant_intensity, std::ratio< 1 >, length, std::ratio<-1 > > spectral_intensity
< Represents an SI derived unit of irradiance
Definition core.h:1368
dimension_divide< power, current > voltage
< Represents an SI derived unit of power
Definition core.h:1352
dimension_divide< charge, voltage > capacitance
< Represents an SI derived unit of voltage
Definition core.h:1353
dimension_multiply< time, current > charge
< Represents an SI derived unit of pressure
Definition core.h:1349
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:1364
dimension_divide< energy, current > magnetic_flux
< Represents an SI derived unit of conductance
Definition core.h:1356
dimension_multiply< force, length > energy
< Represents an SI derived unit of charge
Definition core.h:1350
make_dimension< power, std::ratio< 1 >, volume, std::ratio<-1 > > spectral_irradiance
< Represents an SI derived unit of spectral intensity
Definition core.h:1371
make_dimension< volume, std::ratio<-1 > > concentration
< Represents an SI derived unit of energy density
Definition core.h:1380
make_dimension< data_tag > data
< Represents a unit of concentration
Definition core.h:1381
dimension_multiply< solid_angle, luminous_intensity > luminous_flux
< Represents an SI derived unit of inductance
Definition core.h:1358
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:1382
dimension_pow< length, std::ratio< 2 > > area
< Represents an SI derived unit of force
Definition core.h:1345
make_dimension< radiant_intensity, std::ratio< 1 >, volume, std::ratio<-1 > > spectral_radiance
< Represents an SI derived unit of spectral flux
Definition core.h:1370
make_dimension< luminous_flux, std::ratio< 1 >, length, std::ratio<-2 > > illuminance
< Represents an SI derived unit of luminous flux
Definition core.h:1359
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:1367
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:1379
make_dimension< length, std::ratio< 1 >, time, std::ratio<-3 > > jerk
< Represents an SI derived unit of spectral irradiance
Definition core.h:1374
dimension_divide< volume, time > volume_flow_rate
< Represents an SI derived unit of volume
Definition core.h:1347
dimension_divide< angular_acceleration, time > angular_jerk
< Represents an SI derived unit of angular acceleration
Definition core.h:1343
dimension_multiply< force, length > torque
< Represents an SI derived unit of jerk
Definition core.h:1375
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:2064
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:1457
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:2565
Unit Conversion Library namespace.
Definition units.h:108
decibels() -> decibels< double >
Nullary guide so bare default-construction decibels{} / decibels() deduces decibels<default> — again ...
constexpr auto clamp(const UnitTypeValue &value, const UnitTypeLo &lo, const UnitTypeHi &hi)
Clamps a value to the range [lo, hi], in the value's own unit when that is lossless (matching min/max...
Definition core.h:5279
Definition core.h:1141
Type representing an arbitrary conversion factor between units.
Definition core.h:1568
numerical scale which is decibel
Definition core.h:5087
static T linearize(const T value) noexcept
linearizes value
Definition core.h:5095
static T scale(const T value) noexcept
returns value in dB
Definition core.h:5113
dimensionless unit with decibel scale
Definition core.h:5127
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:3237
is_unit implementation: an incomplete or non-class type is never a unit, decided WITHOUT instantiatin...
Definition core.h:901
Definition core.h:3275
Definition core.h:3313
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:3851
static constexpr T scale(const T value) noexcept
scales value
Definition core.h:3871
static constexpr T linearize(const T value) noexcept
linearizes value
Definition core.h:3859
Tag for unit constructors.
Definition core.h:2223
Definition core.h:1238
Definition core.h:1123
Trait which tests whether a type is inherited from a decibel scale.
Definition core.h:3825
Trait which tests whether a type is inherited from a linear scale.
Definition core.h:3810
BinaryTypeTrait for querying whether Cf1 and Cf2 are conversion factors to the same dimension.
Definition core.h:2050
BinaryTypeTrait for querying whether U1 and U2 are units of the same dimension.
Definition core.h:2580
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:1503
SFINAE-able trait that maps a conversion_factor to its strengthened type.
Definition core.h:1076
Definition core.h:181
Definition core.h:175