mirror of
https://github.com/ETLCPP/etl.git
synced 2026-10-28 18:07:10 +08:00
Support runtime floating-point clamped_value before C++20 (#1595)
This commit is contained in:
parent
2696828f27
commit
ffea27142a
@ -10,26 +10,27 @@ title: "clamped_value"
|
|||||||
Provides a value constrained between a minimum and maximum value. Values are
|
Provides a value constrained between a minimum and maximum value. Values are
|
||||||
clamped using `etl::clamp`. The type supports compile-time bounds to minimise
|
clamped using `etl::clamp`. The type supports compile-time bounds to minimise
|
||||||
storage and runtime bounds when the range must be changed. Integral types are
|
storage and runtime bounds when the range must be changed. Integral types are
|
||||||
supported in all language modes. Floating-point types are supported in C++20
|
supported in all language modes. Floating-point types with runtime bounds are
|
||||||
when `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is `1`. By default, the macro is
|
supported in C++11 and later when `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is
|
||||||
enabled when `__cpp_nontype_template_args` is at least `201911L`.
|
`1`. Floating-point compile-time bounds require C++20 support for
|
||||||
|
floating-point non-type template arguments.
|
||||||
|
|
||||||
## Availability
|
## Availability
|
||||||
|
|
||||||
`ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is always defined by this header as
|
`ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is always defined by this header as
|
||||||
either `0` or `1`. It defaults to `1` only when C++20 is enabled and the
|
either `0` or `1`. It defaults to `1` in C++11 and later. The related
|
||||||
compiler supports floating-point non-type template arguments. A platform
|
`ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE` macro defaults to `1` only
|
||||||
profile or build may define the macro before including the header to override
|
when C++20 is enabled and the compiler supports floating-point non-type
|
||||||
automatic detection.
|
template arguments. A platform profile or build may define either macro before
|
||||||
|
including the header to override automatic detection.
|
||||||
|
|
||||||
When the macro is `0`, `T` must be an integral type. When it is `1`, `float`,
|
When `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is `0`, `T` must be an integral
|
||||||
`double`, and `long double` may also be used.
|
type. When it is `1`, `float`, `double`, and `long double` may also be used with
|
||||||
|
runtime bounds.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template <typename T,
|
// C++11 and later
|
||||||
T Min = T(),
|
template <typename T, T... Limits>
|
||||||
T Max = T(),
|
|
||||||
bool RuntimeSpecialisation = ((Min == T()) && (Max == T()))>
|
|
||||||
class clamped_value;
|
class clamped_value;
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -39,8 +40,11 @@ etl::clamped_value<int, 2, 7> value_ct; // Fixed range [2, 7].
|
|||||||
etl::clamped_value<int> value_rt(2, 7, 5); // Runtime range and initial value.
|
etl::clamped_value<int> value_rt(2, 7, 5); // Runtime range and initial value.
|
||||||
|
|
||||||
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
||||||
|
etl::clamped_value<double> float_rt(-1.5, 2.5, 0.5);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE
|
||||||
etl::clamped_value<float, -1.5f, 2.5f> float_ct(0.5f);
|
etl::clamped_value<float, -1.5f, 2.5f> float_ct(0.5f);
|
||||||
etl::clamped_value<double> float_rt(-1.5, 2.5, 0.5);
|
|
||||||
#endif
|
#endif
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -67,13 +71,10 @@ clamped_value<int> value(min, max, initial);
|
|||||||
Creates a runtime clamped value. The two-argument form initializes the value
|
Creates a runtime clamped value. The two-argument form initializes the value
|
||||||
to `min`; the three-argument form clamps `initial` to the range.
|
to `min`; the three-argument form clamps `initial` to the range.
|
||||||
|
|
||||||
`min` must not be greater than `max`. As with `cyclic_value`, zero-initialized
|
`min` must not be greater than `max`. Omitting the template bounds selects the
|
||||||
`Min` and `Max` select the runtime-bound specialization; use a non-zero
|
runtime-bound specialization. Supplying `Min` and `Max` selects fixed
|
||||||
compile-time bound when a fixed compile-time range is needed. NaN bounds and
|
compile-time bounds. NaN bounds and initial values are rejected. Infinite
|
||||||
initial values are rejected. Infinite bounds and values are permitted.
|
bounds and values are permitted.
|
||||||
|
|
||||||
Because zero-initialized `Min` and `Max` select runtime bounds, a fixed
|
|
||||||
compile-time range `[0, 0]` cannot be represented by this dispatch scheme.
|
|
||||||
|
|
||||||
## Modifiers
|
## Modifiers
|
||||||
|
|
||||||
|
|||||||
@ -43,21 +43,32 @@ SOFTWARE.
|
|||||||
|
|
||||||
///\def ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
///\def ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
||||||
/// Set to `1` when clamped_value supports floating-point types.
|
/// Set to `1` when clamped_value supports floating-point types.
|
||||||
/// Floating-point support requires C++20 non-type template arguments.
|
/// Runtime floating-point support requires C++11 variadic templates.
|
||||||
/// The macro may be defined by the build or platform profile to override
|
/// The macro may be defined by the build or platform profile to override
|
||||||
/// automatic detection.
|
/// automatic detection.
|
||||||
#if !defined(ETL_HAS_FLOATING_POINT_CLAMPED_VALUE)
|
#if !defined(ETL_HAS_FLOATING_POINT_CLAMPED_VALUE)
|
||||||
#if ETL_USING_CPP20 && defined(__cpp_nontype_template_args) && (__cpp_nontype_template_args >= 201911L)
|
#if ETL_USING_CPP11
|
||||||
#define ETL_HAS_FLOATING_POINT_CLAMPED_VALUE 1
|
#define ETL_HAS_FLOATING_POINT_CLAMPED_VALUE 1
|
||||||
#else
|
#else
|
||||||
#define ETL_HAS_FLOATING_POINT_CLAMPED_VALUE 0
|
#define ETL_HAS_FLOATING_POINT_CLAMPED_VALUE 0
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
///\def ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE
|
||||||
|
/// Set to `1` when clamped_value supports floating-point compile-time bounds.
|
||||||
|
#if !defined(ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE)
|
||||||
|
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE && ETL_USING_CPP20 && defined(__cpp_nontype_template_args) && (__cpp_nontype_template_args >= 201911L)
|
||||||
|
#define ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE 1
|
||||||
|
#else
|
||||||
|
#define ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
///\defgroup clamped_value clamped_value
|
///\defgroup clamped_value clamped_value
|
||||||
/// Provides a value that is clamped between two limits.
|
/// Provides a value that is clamped between two limits.
|
||||||
/// Integral types are supported in all language modes. Floating-point types
|
/// Integral types are supported in all language modes. Floating-point types
|
||||||
/// are supported when ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`.
|
/// with runtime bounds are supported when
|
||||||
|
/// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`.
|
||||||
/// \ingroup utilities
|
/// \ingroup utilities
|
||||||
|
|
||||||
namespace etl
|
namespace etl
|
||||||
@ -113,18 +124,18 @@ namespace etl
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned_type current = etl::to_unsigned(value); //static_cast<unsigned_type>(value);
|
const unsigned_type current = etl::to_unsigned(value); // static_cast<unsigned_type>(value);
|
||||||
const unsigned_type step = etl::absolute_unsigned(n);
|
const unsigned_type step = etl::absolute_unsigned(n);
|
||||||
|
|
||||||
if ((n > 0) && (value < max_value))
|
if ((n > 0) && (value < max_value))
|
||||||
{
|
{
|
||||||
const unsigned_type distance = etl::to_unsigned(max_value) - current; //static_cast<unsigned_type>(max_value) - current;
|
const unsigned_type distance = etl::to_unsigned(max_value) - current; // static_cast<unsigned_type>(max_value) - current;
|
||||||
|
|
||||||
value = (step >= distance) ? max_value : static_cast<T>(current + step);
|
value = (step >= distance) ? max_value : static_cast<T>(current + step);
|
||||||
}
|
}
|
||||||
else if ((n < 0) && (value > min_value))
|
else if ((n < 0) && (value > min_value))
|
||||||
{
|
{
|
||||||
const unsigned_type distance = current - etl::to_unsigned(min_value); //static_cast<unsigned_type>(min_value);
|
const unsigned_type distance = current - etl::to_unsigned(min_value); // static_cast<unsigned_type>(min_value);
|
||||||
|
|
||||||
value = (step >= distance) ? min_value : static_cast<T>(current - step);
|
value = (step >= distance) ? min_value : static_cast<T>(current - step);
|
||||||
}
|
}
|
||||||
@ -236,22 +247,32 @@ namespace etl
|
|||||||
} // namespace private_clamped_value
|
} // namespace private_clamped_value
|
||||||
/// \endcond
|
/// \endcond
|
||||||
|
|
||||||
#include "private/diagnostic_float_equal_push.h"
|
#if ETL_USING_CPP11
|
||||||
|
template <typename T, T... Limits>
|
||||||
|
class clamped_value;
|
||||||
|
#else // ETL_NOT_USING_CPP11
|
||||||
|
#include "private/diagnostic_float_equal_push.h"
|
||||||
template <typename T, T Min = T(), T Max = T(), bool RuntimeSpecialisation = ((Min == T()) && (Max == T()))>
|
template <typename T, T Min = T(), T Max = T(), bool RuntimeSpecialisation = ((Min == T()) && (Max == T()))>
|
||||||
class clamped_value;
|
class clamped_value;
|
||||||
#include "private/diagnostic_pop.h"
|
#include "private/diagnostic_pop.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
//***************************************************************************
|
//***************************************************************************
|
||||||
/// Provides a value that is clamped between two compile-time limits.
|
/// Provides a value that is clamped between two compile-time limits.
|
||||||
/// Supports incrementing, decrementing and arbitrary advance.
|
/// Supports incrementing, decrementing and arbitrary advance.
|
||||||
///@tparam T An integral type, or a floating-point type when
|
///@tparam T An integral type, or a floating-point type when
|
||||||
/// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`.
|
/// ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE is `1`.
|
||||||
///@tparam Min The minimum value of the range.
|
///@tparam Min The minimum value of the range.
|
||||||
///@tparam Max The maximum value of the range.
|
///@tparam Max The maximum value of the range.
|
||||||
///\ingroup clamped_value
|
///\ingroup clamped_value
|
||||||
//***************************************************************************
|
//***************************************************************************
|
||||||
|
#if ETL_USING_CPP11
|
||||||
|
template <typename T, T Min, T Max>
|
||||||
|
class clamped_value<T, Min, Max>
|
||||||
|
#else // ETL_NOT_USING_CPP11
|
||||||
template <typename T, T Min, T Max>
|
template <typename T, T Min, T Max>
|
||||||
class clamped_value<T, Min, Max, false>
|
class clamped_value<T, Min, Max, false>
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -583,8 +604,13 @@ namespace etl
|
|||||||
///@tparam Max Ignored for this specialisation.
|
///@tparam Max Ignored for this specialisation.
|
||||||
///\ingroup clamped_value
|
///\ingroup clamped_value
|
||||||
//***************************************************************************
|
//***************************************************************************
|
||||||
|
#if ETL_USING_CPP11
|
||||||
|
template <typename T>
|
||||||
|
class clamped_value<T>
|
||||||
|
#else // ETL_NOT_USING_CPP11
|
||||||
template <typename T, T Min, T Max>
|
template <typename T, T Min, T Max>
|
||||||
class clamped_value<T, Min, Max, true>
|
class clamped_value<T, Min, Max, true>
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,9 @@ SOFTWARE.
|
|||||||
#include <etl/clamped_value.h>
|
#include <etl/clamped_value.h>
|
||||||
|
|
||||||
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
||||||
etl::clamped_value<float, -1.0f, 1.0f> clamped_float_compile_time;
|
etl::clamped_value<double> clamped_double_run_time;
|
||||||
etl::clamped_value<double> clamped_double_run_time;
|
#endif
|
||||||
|
|
||||||
|
#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE
|
||||||
|
etl::clamped_value<float, -1.0f, 1.0f> clamped_float_compile_time;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -61,6 +61,17 @@ namespace
|
|||||||
CHECK_EQUAL(7, value.max());
|
CHECK_EQUAL(7, value.max());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ETL_USING_CPP11
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(test_compile_time_zero_range)
|
||||||
|
{
|
||||||
|
etl::clamped_value<int, 0, 0> value(1);
|
||||||
|
CHECK_EQUAL(0, value.get());
|
||||||
|
CHECK_EQUAL(0, value.min());
|
||||||
|
CHECK_EQUAL(0, value.max());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
TEST(test_run_time_initialisation)
|
TEST(test_run_time_initialisation)
|
||||||
{
|
{
|
||||||
@ -364,7 +375,7 @@ namespace
|
|||||||
CHECK_THROW(value.set(7, 2), etl::exception);
|
CHECK_THROW(value.set(7, 2), etl::exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
TEST(test_compile_time_floating_clamps_and_advances)
|
TEST(test_compile_time_floating_clamps_and_advances)
|
||||||
{
|
{
|
||||||
@ -381,7 +392,9 @@ namespace
|
|||||||
value.advance(10.0f);
|
value.advance(10.0f);
|
||||||
CHECK_CLOSE(2.5f, value.get(), 0.0001f);
|
CHECK_CLOSE(2.5f, value.get(), 0.0001f);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
TEST(test_runtime_floating_clamps_and_advances)
|
TEST(test_runtime_floating_clamps_and_advances)
|
||||||
{
|
{
|
||||||
@ -396,45 +409,52 @@ namespace
|
|||||||
value.advance(-10.0);
|
value.advance(-10.0);
|
||||||
CHECK_CLOSE(-1.5, value.get(), 0.0001);
|
CHECK_CLOSE(-1.5, value.get(), 0.0001);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
TEST(test_floating_increment_and_decrement_saturate_at_fractional_bounds)
|
TEST(test_compile_time_floating_increment_saturates_at_fractional_bound)
|
||||||
{
|
{
|
||||||
etl::clamped_value<float, -0.5f, 0.5f> compile_time(0.25f);
|
etl::clamped_value<float, -0.5f, 0.5f> compile_time(0.25f);
|
||||||
etl::clamped_value<float> run_time(-0.5f, 0.5f, -0.25f);
|
|
||||||
|
|
||||||
++compile_time;
|
++compile_time;
|
||||||
--run_time;
|
|
||||||
|
|
||||||
CHECK_CLOSE(0.5f, compile_time.get(), 0.0001f);
|
CHECK_CLOSE(0.5f, compile_time.get(), 0.0001f);
|
||||||
CHECK_CLOSE(-0.5f, run_time.get(), 0.0001f);
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
||||||
|
//*************************************************************************
|
||||||
|
TEST(test_runtime_floating_decrement_saturates_at_fractional_bound)
|
||||||
|
{
|
||||||
|
etl::clamped_value<float> value(-0.5f, 0.5f, -0.25f);
|
||||||
|
|
||||||
|
--value;
|
||||||
|
|
||||||
|
CHECK_CLOSE(-0.5f, value.get(), 0.0001f);
|
||||||
}
|
}
|
||||||
|
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
TEST(test_floating_comparison_and_swap)
|
TEST(test_runtime_floating_comparison_and_swap)
|
||||||
{
|
{
|
||||||
etl::clamped_value<double, -2.0, 2.0> compile_time1(-0.5);
|
etl::clamped_value<double> value1(-3.0, 3.0, -1.0);
|
||||||
etl::clamped_value<double, -2.0, 2.0> compile_time2(1.5);
|
etl::clamped_value<double> value2(4.0, 8.0, 6.0);
|
||||||
etl::clamped_value<double> run_time1(-3.0, 3.0, -1.0);
|
|
||||||
etl::clamped_value<double> run_time2(4.0, 8.0, 6.0);
|
|
||||||
|
|
||||||
CHECK(compile_time1 < compile_time2);
|
CHECK(value1 < value2);
|
||||||
CHECK(compile_time1 < 1.0);
|
CHECK(value1 < 1.0);
|
||||||
swap(compile_time1, compile_time2);
|
swap(value1, value2);
|
||||||
swap(run_time1, run_time2);
|
|
||||||
|
|
||||||
CHECK_CLOSE(1.5, compile_time1.get(), 0.0001);
|
CHECK_CLOSE(6.0, value1.get(), 0.0001);
|
||||||
CHECK_CLOSE(6.0, run_time1.get(), 0.0001);
|
CHECK_CLOSE(4.0, value1.min(), 0.0001);
|
||||||
CHECK_CLOSE(4.0, run_time1.min(), 0.0001);
|
CHECK_CLOSE(-1.0, value2.get(), 0.0001);
|
||||||
CHECK_CLOSE(-1.0, run_time2.get(), 0.0001);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
TEST(test_floating_infinity_saturates)
|
TEST(test_runtime_floating_infinity_saturates)
|
||||||
{
|
{
|
||||||
const double infinity = etl::numeric_limits<double>::infinity();
|
const double infinity = etl::numeric_limits<double>::infinity();
|
||||||
etl::clamped_value<double, -2.0, 2.0> value(0.0);
|
etl::clamped_value<double> value(-2.0, 2.0, 0.0);
|
||||||
etl::clamped_value<double> infinite_range(-infinity, infinity, 0.0);
|
etl::clamped_value<double> infinite_range(-infinity, infinity, 0.0);
|
||||||
|
|
||||||
value += infinity;
|
value += infinity;
|
||||||
CHECK_CLOSE(2.0, value.get(), 0.0001);
|
CHECK_CLOSE(2.0, value.get(), 0.0001);
|
||||||
@ -501,7 +521,7 @@ namespace
|
|||||||
CHECK(true);
|
CHECK(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE && (ETL_USING_MICROSOFT_COMPILER && (_MSC_VER >= 1950))
|
#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE && (ETL_USING_MICROSOFT_COMPILER && (_MSC_VER >= 1950))
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
TEST(test_floating_clamped_value_constexpr)
|
TEST(test_floating_clamped_value_constexpr)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user