micro-test-plus 6.0.0
µTest++ Testing Framework
Loading...
Searching...
No Matches
micro_os_plus::micro_test_plus::detail::math Namespace Reference

Internal mathematical utilities for the µTest++ testing framework. More...

Classes

struct  integer_literal
 The result of parsing the characters of an integer literal. More...

Functions

template<class T>
constexpr auto abs (const T t) noexcept -> T
 Computes the absolute value of a given comparable value.
template<class T, class Value_T>
constexpr auto count_decimals (Value_T value) -> T
 Computes the number of decimal places of a value, with a precision of about seven digits.
template<bool Fraction, char... Cs>
consteval auto count_literal_digits (void) -> std::size_t
 Counts the decimal digits of a floating-point literal, before or after the decimal point.
template<class Value_T>
constexpr auto decimal_epsilon (Value_T value) -> Value_T
 Computes the default comparison tolerance of a floating-point value, one unit of its last decimal.
template<class T, char... Cs>
consteval auto den (void) -> T
 Computes the decimal part of a number represented as an array of characters.
template<class T, char... Cs>
consteval auto den_size (void) -> T
 Counts the decimal places of a number represented as an array of characters.
template<class T, char... Cs>
consteval auto integer_magnitude (void) -> unsigned long long
 Computes the value of an integer literal of type T, before any negation.
template<class T>
constexpr auto min_value (const T &lhs, const T &rhs) noexcept -> const T &
 Computes the minimum of two comparable values.
template<class T, char... Cs>
consteval auto num (void) -> T
 Computes the integral part of a decimal floating-point number represented as an array of characters.
template<char... Cs>
consteval auto parse_integer_literal (void) -> integer_literal
 Parses the characters of an integer literal.
template<class T, class Exp_T>
constexpr auto pow (const T base, const Exp_T exp) noexcept -> T
 Generic exponentiation function to compute the power of a base raised to an exponent.

Detailed Description

The detail::math namespace offers a suite of constexpr mathematical function templates and utilities, used by the literals and by the floating-point comparisons of the µTest++ framework.

These functions include generic implementations for absolute value, minimum value, exponentiation, and compile-time parsing of numeric values from character arrays. The utilities are designed to be lightweight and suitable for embedded environments, where standard library alternatives may be unavailable, less efficient, or not constexpr.

The names of these functions suggest general mathematical functions, but their scope is limited to the needs of the framework (for example, pow() accepts only integral exponents); like everything in detail, they are not part of the public API and may be modified without prior notice.

Function Documentation

◆ abs()

template<class T>
auto micro_os_plus::micro_test_plus::detail::math::abs ( const T t) -> T
nodiscardconstexprnoexcept

This function template provides a generic, constexpr implementation for obtaining the absolute value of any type that supports comparison and unary negation.

The function returns the non-negative value of the input. If the input is less than the default-constructed value of its type (typically zero), the negated value is returned; otherwise, the original value is returned.

Template Parameters
TThe type of the input value. Must support comparison and unary negation.
Parameters
tThe value for which the absolute value is to be computed.
Returns
The absolute value of the input.

Definition at line 88 of file math-inlines.h.

89 {
90 return t < T{} ? -t : t;
91 }

References abs().

Referenced by abs(), count_decimals(), micro_os_plus::micro_test_plus::detail::differ_by_at_most(), and micro_os_plus::micro_test_plus::detail::within_tolerance().

◆ count_decimals()

template<class T, class Value_T>
auto micro_os_plus::micro_test_plus::detail::math::count_decimals ( Value_T value) -> T
nodiscardconstexpr
Template Parameters
TThe integral type for the result.
Value_TThe type of the input value, a floating-point type.
Parameters
valueThe value whose decimal precision is to be determined; it may be negative, infinite, or a NaN.
Returns
The number of decimal places, as a value of type T, at least one, or zero for a zero value; the leading zeros of a value below one are counted; it is limited by the magnitude from which Value_T represents only integers.

This function template determines the number of decimal (fractional) digits present in a floating-point value, with a precision of about seven digits. Being constexpr, it can also be evaluated at compile time.

The function repeatedly multiplies the absolute value by ten, incrementing a counter until the distance to the nearest integer is at most a defined precision threshold (1e-7). Using the distance to the nearest integer, not the fractional part, prevents the rounding noise of a value just below an integer (e.g. 2674.9999999999995) from being taken as more decimals.

The nearest integer is never zero: while the scaled value is below one, its distance to one is used, so that the leading zeros of a small value are counted as decimals, as in its notation (12 for 1e-12), instead of the count stopping as soon as the scaled value is below the threshold. A zero value has no significant digit, and the result is zero.

The loop also stops when the value reaches the magnitude from which the type can represent only integers (2^23 for float, 2^52 for double), limited to 2^63 so that the conversion to unsigned long long is always defined. This bounds the number of steps for large values, infinities, and NaNs, for which the result is one; for small values, the number of steps is bounded by the decimal exponent of the smallest value of the type.

Definition at line 258 of file deprecated-inlines.h.

259 {
260 constexpr auto precision = Value_T (1e-7);
261 // From this magnitude upwards all values are integers.
262 constexpr auto integral_limit
263 = min_value (Value_T (1) / std::numeric_limits<Value_T>::epsilon (),
264 Value_T (1ULL << 63));
265
266 value = abs (value);
267
268#if defined(__GNUC__)
269#pragma GCC diagnostic push
270#pragma GCC diagnostic ignored "-Wfloat-equal"
271#endif // defined(__GNUC__)
272 if (value == Value_T (0))
273 {
274 // A zero has no decimals.
275 return T{};
276 }
277#if defined(__GNUC__)
278#pragma GCC diagnostic pop
279#endif // defined(__GNUC__)
280
281 T result{};
282 for (;;)
283 {
284 value *= 10;
285 ++result;
286
287 // Also true for infinities and NaNs.
288 if (not(value < integral_limit))
289 {
290 break;
291 }
292
293 const auto fraction = value
294 - static_cast<Value_T> (
295 static_cast<unsigned long long> (value));
296 // Below one, the distance is to one, not to zero, so that the
297 // leading zeros of a small value are counted.
298 const auto distance
299 = (value < Value_T (1))
300 ? Value_T (1) - fraction
301 : min_value (fraction, Value_T (1) - fraction);
302 if (distance <= precision)
303 {
304 break;
305 }
306 }
307
308 return result;
309 }
constexpr auto abs(const T t) noexcept -> T
Computes the absolute value of a given comparable value.
constexpr auto min_value(const T &lhs, const T &rhs) noexcept -> const T &
Computes the minimum of two comparable values.
Struct template representing a generic value, accessible via a getter.
Definition deprecated.h:629

References abs(), count_decimals(), and min_value().

Referenced by count_decimals(), and decimal_epsilon().

◆ count_literal_digits()

template<bool Fraction, char... Cs>
auto micro_os_plus::micro_test_plus::detail::math::count_literal_digits ( void ) -> std::size_t
nodiscardconsteval

The digit separators (') are not counted.

Template Parameters
Fractiontrue to count the digits after the decimal point, false to count those before it.
CsThe characters of the literal.
Parameters
None.
Returns
The number of digits.

Walks the characters once, counting the decimal digits before the first decimal point (Fraction false) or after it (Fraction true); the digit separators are skipped.

Definition at line 101 of file deprecated-inlines.h.

102 {
103 std::size_t count = 0;
104 bool is_fraction = false;
105 for (const char c : { Cs... })
106 {
107 if (c == '.')
108 {
109 is_fraction = true;
110 }
111 else if (c >= '0' and c <= '9' and is_fraction == Fraction)
112 {
113 ++count;
114 }
115 }
116 return count;
117 }

References count_literal_digits().

Referenced by count_literal_digits(), den(), den_size(), and num().

◆ decimal_epsilon()

template<class Value_T>
auto micro_os_plus::micro_test_plus::detail::math::decimal_epsilon ( Value_T value) -> Value_T
nodiscardconstexpr
Template Parameters
Value_TThe type of the value, a floating-point type.
Parameters
valueThe value whose tolerance is to be determined; it may be negative, infinite, or a NaN.
Returns
Ten raised to minus the number of decimal places returned by count_decimals() (0.01 for 1.25, 1e-12 for 1e-12), or zero, which means an exact comparison, for a zero value and for a value whose tolerance would be below the range of Value_T.

The number of decimal places is computed by count_decimals(), and the tolerance is one divided by ten raised to that number, as for the floating-point literals, which use the decimals written.

A zero value has no decimals, and gets a zero tolerance, so that it is compared exactly; a tolerance of one unit of an arbitrary decimal would make the zero equal to unrelated small values. A zero tolerance is also returned when ten raised to the number of decimals would exceed the range of Value_T (values below about 1e-308 for double), instead of computing an infinite power.

Definition at line 326 of file deprecated-inlines.h.

327 {
328 const auto decimals = count_decimals<unsigned long long> (value);
329 if (decimals == 0
330 or decimals > static_cast<unsigned long long> (
331 std::numeric_limits<Value_T>::max_exponent10))
332 {
333 return Value_T{};
334 }
335 return Value_T (1) / pow (Value_T (10), decimals);
336 }
constexpr auto pow(const T base, const Exp_T exp) noexcept -> T
Generic exponentiation function to compute the power of a base raised to an exponent.
constexpr auto count_decimals(Value_T value) -> T
Computes the number of decimal places of a value, with a precision of about seven digits.

References count_decimals(), decimal_epsilon(), and pow().

Referenced by decimal_epsilon().

◆ den()

template<class T, char... Cs>
auto micro_os_plus::micro_test_plus::detail::math::den ( void ) -> T
nodiscardconsteval
Template Parameters
TThe target integral type for the result.
CsThe character pack representing the numeric value; it must contain a decimal point.
Parameters
None.
Returns
The digits after the decimal point, as an integral value of type T (e.g. 25 for 1.25), to be divided by ten raised to the power returned by den_size().

This function template performs compile-time extraction of the decimal (fractional) part from a sequence of characters, typically provided as a template parameter pack.

The function expects the character sequence to represent a numeric value, where all characters are either digits, a dot (.), or an apostrophe ('). Parsing begins after the first dot, accumulating the decimal digits as an integer value and skipping the digit separators (e.g. 1.25 yields 25), so that den() / 10^den_size() gives the fraction. If the result could exceed T, the compilation fails with an explicit message.

Definition at line 177 of file deprecated-inlines.h.

178 {
179 static_assert ((... || (Cs == '.')),
180 "den(): no decimal point in literal");
181 // The result has as many digits as the decimals, and it must fit in
182 // `T`.
183 static_assert (
184 count_literal_digits<true, Cs...> ()
185 <= static_cast<std::size_t> (std::numeric_limits<T>::digits10),
186 "µTest++: the floating-point literal has too many "
187 "decimals");
188 T result{};
189 bool is_fraction = false;
190 for (const char c : { Cs... })
191 {
192 if (c == '.')
193 {
194 is_fraction = true;
195 }
196 else if (is_fraction and c >= '0' and c <= '9')
197 {
198 // The digit separators are skipped.
199 result = static_cast<T> (result * static_cast<T> (10)
200 + static_cast<T> (c - '0'));
201 }
202 }
203 return result;
204 }
consteval auto count_literal_digits(void) -> std::size_t
Counts the decimal digits of a floating-point literal, before or after the decimal point.

References count_literal_digits(), and den().

Referenced by den(), micro_os_plus::micro_test_plus::literals::operator""_d(), micro_os_plus::micro_test_plus::literals::operator""_f(), and micro_os_plus::micro_test_plus::literals::operator""_ld().

◆ den_size()

template<class T, char... Cs>
auto micro_os_plus::micro_test_plus::detail::math::den_size ( void ) -> T
nodiscardconsteval
Template Parameters
TThe integral type for the result.
CsThe character pack representing the numeric value; it must contain a decimal point.
Parameters
None.
Returns
The number of decimal places (e.g. 2 for 1.25), as a value of type T; it is the scale of the value returned by den().

This function template determines, at compile time, the number of decimal (fractional) digits present in a numeric value represented by a character sequence, typically provided as a template parameter pack.

The function expects the character sequence to represent a numeric value, where all characters are either digits, a dot (.), or an apostrophe ('). It returns the number of digits that follow the first dot (the digit separators are not counted; e.g. 2 for 1.25), which is the scale of the value returned by den().

Definition at line 220 of file deprecated-inlines.h.

221 {
222 static_assert ((... || (Cs == '.')),
223 "den_size(): no decimal point in literal");
224 return static_cast<T> (count_literal_digits<true, Cs...> ());
225 }

References count_literal_digits(), and den_size().

Referenced by den_size(), micro_os_plus::micro_test_plus::literals::operator""_d(), micro_os_plus::micro_test_plus::literals::operator""_f(), and micro_os_plus::micro_test_plus::literals::operator""_ld().

◆ integer_magnitude()

template<class T, char... Cs>
auto micro_os_plus::micro_test_plus::detail::math::integer_magnitude ( void ) -> unsigned long long
nodiscardconsteval
Template Parameters
TThe integral type of the literal.
CsThe characters of the literal, as received by a literal operator template.
Parameters
None.
Returns
The value of the literal, up to max() of T, or, for the signed types, up to max () + 1, the absolute value of the minimum.

The literal must be an integer literal, in any of the C++ forms (decimal, hexadecimal, binary, or octal, with digit separators), and its value must fit in T; otherwise the compilation fails with an explicit message.

Negative values are obtained with the unary minus, so the literal itself is never negative. For the signed types, the limit is the absolute value of the minimum, max () + 1, so that the minimum can be written (e.g. -128_i8); the caller allows this magnitude only when it is negated.

When a check fails, zero is returned, so that the callers report no further errors.

Definition at line 448 of file deprecated-inlines.h.

449 {
450 constexpr integer_literal literal = parse_integer_literal<Cs...> ();
451 constexpr unsigned long long max_magnitude
452 = static_cast<unsigned long long> (std::numeric_limits<T>::max ())
453 + (std::numeric_limits<T>::is_signed ? 1u : 0u);
454 constexpr bool fits = literal.is_integer && !literal.is_too_large
455 && literal.value <= max_magnitude;
456
457 static_assert (literal.is_integer,
458 "µTest++: an integer literal must not have a decimal "
459 "point or an exponent");
460 static_assert (!literal.is_integer || !literal.is_too_large,
461 "µTest++: the integer literal is too large");
462 static_assert (!literal.is_integer || literal.is_too_large || fits,
463 "µTest++: the integer literal value does not fit in the "
464 "type of the literal");
465
466 return fits ? literal.value : 0;
467 }
consteval auto parse_integer_literal(void) -> integer_literal
Parses the characters of an integer literal.
The result of parsing the characters of an integer literal.
Definition deprecated.h:258
unsigned long long value
The value of the literal.
Definition deprecated.h:265
bool is_too_large
Whether the value exceeds unsigned long long.
Definition deprecated.h:282
bool is_integer
Whether the characters form an integer literal.
Definition deprecated.h:274

References integer_magnitude(), micro_os_plus::micro_test_plus::detail::math::integer_literal::is_integer, micro_os_plus::micro_test_plus::detail::math::integer_literal::is_too_large, parse_integer_literal(), and micro_os_plus::micro_test_plus::detail::math::integer_literal::value.

Referenced by integer_magnitude(), and micro_os_plus::micro_test_plus::detail::integral_literal().

◆ min_value()

template<class T>
auto micro_os_plus::micro_test_plus::detail::math::min_value ( const T & lhs,
const T & rhs ) -> const T &
nodiscardconstexprnoexcept

This function template provides a generic, constexpr implementation for determining the minimum of two values of any type that supports comparison.

The function returns a reference to the lesser of the two input values, as determined by the < operator. If the second argument is less than the first, it is returned; otherwise, the first argument is returned.

Template Parameters
TThe type of the input values. Must support comparison via the < operator.
Parameters
lhsThe first value to compare.
rhsThe second value to compare.
Returns
A reference to the minimum of the two input values.

Definition at line 106 of file math-inlines.h.

107 {
108 return (rhs < lhs) ? rhs : lhs;
109 }

References min_value().

Referenced by count_decimals(), min_value(), and micro_os_plus::micro_test_plus::detail::within_tolerance().

◆ num()

template<class T, char... Cs>
auto micro_os_plus::micro_test_plus::detail::math::num ( void ) -> T
nodiscardconsteval

Used by the floating-point literal operators; the integer literals use integer_magnitude(), which also accepts the C++ prefixes and checks the range.

Template Parameters
TThe target integral type for the result.
CsThe character pack representing the numeric value.
Parameters
None.
Returns
The parsed integral value of type T.

This function template performs compile-time parsing of a numeric value from a sequence of characters, typically provided as a template parameter pack.

The characters must be decimal digits, a dot (.), or digit separators ('), which are skipped; otherwise (e.g. an exponent) the compilation fails with an explicit message, as it does if the integral part has more digits than T can always hold. Parsing stops at the first dot, allowing the function to extract only the integral part of the number.

Definition at line 134 of file deprecated-inlines.h.

135 {
136 static_assert (
137 ((Cs == '.' or Cs == '\'' or (Cs >= '0' and Cs <= '9')) and ...),
138 "µTest++: a floating-point literal must be written with decimal "
139 "digits and a decimal point, without an exponent");
140 static_assert (
141 count_literal_digits<false, Cs...> ()
142 <= static_cast<std::size_t> (std::numeric_limits<T>::digits10),
143 "µTest++: the floating-point literal has too many "
144 "digits before the decimal point");
145 T result{};
146 for (const char c : { Cs... })
147 {
148 if (c == '.')
149 {
150 break;
151 }
152 if (c >= '0' and c <= '9')
153 {
154 result = static_cast<T> (result * static_cast<T> (10)
155 + static_cast<T> (c - '0'));
156 }
157 }
158 return result;
159 }

References count_literal_digits(), and num().

Referenced by num(), micro_os_plus::micro_test_plus::literals::operator""_d(), micro_os_plus::micro_test_plus::literals::operator""_f(), and micro_os_plus::micro_test_plus::literals::operator""_ld().

◆ parse_integer_literal()

template<char... Cs>
auto micro_os_plus::micro_test_plus::detail::math::parse_integer_literal ( void ) -> integer_literal
nodiscardconsteval
Template Parameters
CsThe characters of the literal, as received by a literal operator template.
Parameters
None.
Returns
The value, and whether the characters form an integer literal and whether its value fits in unsigned long long.

The characters are those of a valid C++ literal, already checked by the compiler (digits valid for the base, digit separators in valid positions). The base is given by the prefix: 0x or 0X for hexadecimal, 0b or 0B for binary, a leading 0 for octal, and none for decimal. The digit separators (') are skipped.

Any other character (a decimal point, an exponent, or a digit not valid for the base, as in the floating-point literal 09.5) marks the literal as not an integer. The value is accumulated in unsigned long long, and the accumulation stops if it would overflow.

Definition at line 354 of file deprecated-inlines.h.

355 {
356 constexpr std::array<char, sizeof...(Cs)> cs{ Cs... };
357 constexpr unsigned long long max_value
358 = std::numeric_limits<unsigned long long>::max ();
359
360 integer_literal result{ 0, true, false };
361
362 unsigned long long base = 10;
363 std::size_t i = 0;
364 if (cs.size () > 1 && cs[0] == '0')
365 {
366 if (cs[1] == 'x' || cs[1] == 'X')
367 {
368 base = 16;
369 i = 2;
370 }
371 else if (cs[1] == 'b' || cs[1] == 'B')
372 {
373 base = 2;
374 i = 2;
375 }
376 else
377 {
378 base = 8;
379 i = 1;
380 }
381 }
382
383 for (; i < cs.size (); ++i)
384 {
385 const char c = cs[i];
386 if (c == '\'')
387 {
388 continue;
389 }
390
391 unsigned long long digit = 0;
392 if (c >= '0' && c <= '9')
393 {
394 digit = static_cast<unsigned long long> (c - '0');
395 }
396 else if (base == 16 && c >= 'a' && c <= 'f')
397 {
398 digit = static_cast<unsigned long long> (c - 'a' + 10);
399 }
400 else if (base == 16 && c >= 'A' && c <= 'F')
401 {
402 digit = static_cast<unsigned long long> (c - 'A' + 10);
403 }
404 else
405 {
406 // A decimal point, an exponent, or a suffix character.
407 result.is_integer = false;
408 return result;
409 }
410
411 if (digit >= base)
412 {
413 // Possible only in a floating-point literal, like `09.5`.
414 result.is_integer = false;
415 return result;
416 }
417
418 if (result.value > (max_value - digit) / base)
419 {
420 result.is_too_large = true;
421 return result;
422 }
423
424 result.value = result.value * base + digit;
425 }
426
427 return result;
428 }

References parse_integer_literal().

Referenced by integer_magnitude(), and parse_integer_literal().

◆ pow()

template<class T, class Exp_T>
auto micro_os_plus::micro_test_plus::detail::math::pow ( const T base,
const Exp_T exp ) -> T
nodiscardconstexprnoexcept
Template Parameters
TThe type of the base value. Must support multiplication assignment and construction from an integer.
Exp_TThe type of the exponent. Must be default-constructible (to zero), and support the < and prefix ++ operators.
Parameters
baseThe base value to be raised to the power of exp.
expThe exponent value.
Returns
The result of raising base to the power of exp.

This function template provides a constexpr implementation for raising a base value to a given exponent, supporting any types that allow multiplication.

The function starts from one and multiplies the result by the base exp times. If the exponent is zero (or negative), the function returns one (the multiplicative identity for the type).

Definition at line 85 of file deprecated-inlines.h.

86 {
87 T result{ 1 };
88 for (Exp_T i{}; i < exp; ++i)
89 result *= base;
90 return result;
91 }

References pow().

Referenced by decimal_epsilon(), and pow().