From f8c68308070b13dd8b03d43ffdd66f8018e30606 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Fri, 28 Dec 2018 09:45:35 +0000 Subject: [PATCH] Merge remote-tracking branch 'origin/feature/cumulative_moving_average' into development --- include/etl/absolute.h | 60 ++++ include/etl/cumulative_moving_average.h | 176 ++++++++++++ include/etl/scaled_rounding.h | 356 ++++++++++++++++++++++++ include/etl/version.h | 4 +- support/Release notes.txt | 11 + test/codeblocks/ETL.cbp | 1 + test/test_cumulative_moving_average.cpp | 125 +++++++++ test/test_scaled_rounding.cpp | 345 +++++++++++++++++++++++ test/vs2017/etl.vcxproj | 5 + test/vs2017/etl.vcxproj.filters | 16 +- 10 files changed, 1095 insertions(+), 4 deletions(-) create mode 100644 include/etl/absolute.h create mode 100644 include/etl/cumulative_moving_average.h create mode 100644 include/etl/scaled_rounding.h create mode 100644 test/test_cumulative_moving_average.cpp create mode 100644 test/test_scaled_rounding.cpp diff --git a/include/etl/absolute.h b/include/etl/absolute.h new file mode 100644 index 00000000..165dba9c --- /dev/null +++ b/include/etl/absolute.h @@ -0,0 +1,60 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_ABSOLUTE_INCLUDED +#define ETL_ABSOLUTE_INCLUDED + +#include "type_traits.h" + +namespace etl +{ + //*************************************************************************** + // For signed types. + //*************************************************************************** + template + typename etl::enable_if::value, T>::type + absolute(T value) + { + return (value < T(0)) ? -value : value; + } + + //*************************************************************************** + // For unsigned types. + //*************************************************************************** + template + typename etl::enable_if::value, T>::type + absolute(T value) + { + return value; + } +} + +#endif + diff --git a/include/etl/cumulative_moving_average.h b/include/etl/cumulative_moving_average.h new file mode 100644 index 00000000..40989603 --- /dev/null +++ b/include/etl/cumulative_moving_average.h @@ -0,0 +1,176 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_CUMULATIVE_MOVING_AVERAGE_INCLUDED +#define ETL_CUMULATIVE_MOVING_AVERAGE_INCLUDED + +#include "type_traits.h" + +namespace etl +{ + //*************************************************************************** + /// Cumulative Moving Average + /// \tparam T The sample value type. + /// \tparam SAMPLE_SIZE The number of samples to average over. + /// \tparam SCALING The scaling factor applied to samples. Default = 1. + //*************************************************************************** + template ::value, + const bool IsFloat = std::is_floating_point::value> + class cumulative_moving_average; + + //*************************************************************************** + /// Cumulative Moving Average + /// For integral types. + /// \tparam T The sample value type. + /// \tparam SAMPLE_SIZE The number of samples to average over. + /// \tparam SCALING The scaling factor applied to samples. Default = 1. + //*************************************************************************** + template + class cumulative_moving_average + { + typedef typename etl::conditional::value, int32_t, uint32_t>::type scale_t; + typedef typename etl::conditional::value, int32_t, uint32_t>::type sample_t; + + static const sample_t SAMPLES = static_cast(SAMPLE_SIZE_); + static const scale_t SCALE = static_cast(SCALING_); + + public: + + static const size_t SAMPLE_SIZE = SAMPLE_SIZE_; ///< The number of samples averaged over. + static const size_t SCALING = SCALING_; ///< The sample scaling factor. + + //************************************************************************* + /// Constructor + /// \param initial_value The initial value for the average. + //************************************************************************* + cumulative_moving_average(const T initial_value) + : average(initial_value * SCALE) + { + } + + //************************************************************************* + /// Clears the average. + /// \param initial_value The initial value for the average. + //************************************************************************* + void clear(const T initial_value) + { + average = (initial_value * SCALE); + } + + //************************************************************************* + /// Adds a new sample to the average. + /// \param new_value The value to add. + //************************************************************************* + void add(T new_value) + { + average *= SAMPLES; + average += SCALE * new_value; + average /= SAMPLES + sample_t(1); + } + + //************************************************************************* + /// Gets the current cumulative average. + /// \return The current average. + //************************************************************************* + T value() const + { + return average; + } + + private: + + T average; ///< The current cumulative average. + }; + + //*************************************************************************** + /// Cumulative Moving Average + /// For floating point types. + /// \tparam T The sample value type. + /// \tparam SAMPLE_SIZE The number of samples to average over. + //*************************************************************************** + template + class cumulative_moving_average + { + public: + + static const size_t SAMPLE_SIZE = SAMPLE_SIZE_; + + //************************************************************************* + /// Constructor + /// \param initial_value The initial value for the average. + //************************************************************************* + cumulative_moving_average(const T initial_value) + : sample_size(T(SAMPLE_SIZE_)), + sample_size_plus_1(T(SAMPLE_SIZE_ + 1)), + average(initial_value) + { + } + + //************************************************************************* + /// Clears the average. + /// \param initial_value The initial value for the average. + //************************************************************************* + void clear(const T initial_value) + { + average = initial_value; + } + + //************************************************************************* + /// Adds a new sample to the average. + /// \param new_value The value to add. + //************************************************************************* + void add(const T new_value) + { + average *= sample_size; + average += new_value; + average /= sample_size_plus_1; + } + + //************************************************************************* + /// Gets the current cumulative average. + /// \return The current average. + //************************************************************************* + T value() const + { + return average; + } + + private: + + const T sample_size; ///< The sample size to average over. + const T sample_size_plus_1; ///< One greater than the sample size. + T average; ///< The current cumulative average. + }; +} + +#endif diff --git a/include/etl/scaled_rounding.h b/include/etl/scaled_rounding.h new file mode 100644 index 00000000..bef1d15a --- /dev/null +++ b/include/etl/scaled_rounding.h @@ -0,0 +1,356 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_SCALED_ROUNDING_INCLUDED +#define ETL_SCALED_ROUNDING_INCLUDED + +#include "static_assert.h" +#include "type_traits.h" +#include "absolute.h" + +namespace etl +{ + + template + struct scaled_rounding_t + { + typedef typename etl::conditional::value, int32_t, uint32_t>::type type; + }; + + //***************************************************************************** + /// A set of rounding algorithms for scaled integrals. + /// \tparam T The integral type. + /// \tparam SCALING The scaling factor. + /// + /// \example For emulating fixed point of two decimal places we could use a + /// scaling factor of '100'. To round the result of scaled int calculations + /// using 'Banker's Rounding' we would define this. + /// \code + /// typedef etl::scaled_rounding Rounding; + /// int final_result = Rounding::round_half_even_unscaled(accumulated_result); + /// \endcode + /// \link http://www.clivemaxfield.com/diycalculator/sp-round.shtml + //***************************************************************************** + + //*************************************************************************** + /// Round to more positive integer. + /// \param value Scaled integral. + /// \return Unscaled, rounded integral. + //*************************************************************************** + template + T round_ceiling_unscaled(T value) + { + ETL_STATIC_ASSERT(etl::is_integral::value, "Type must be an integral"); + typedef typename scaled_rounding_t::type scale_t; + + if (value >= 0) + { + return T((value + scale_t(SCALING)) / scale_t(SCALING)); + } + else + { + return T(value / scale_t(SCALING)); + } + } + + //*************************************************************************** + /// Round to more positive integer. + /// \param value Scaled integral. + /// \return Scaled, rounded integral. + //*************************************************************************** + template + T round_ceiling_scaled(T value) + { + typedef typename scaled_rounding_t::type scale_t; + + return round_ceiling_unscaled(value) * scale_t(SCALING); + } + + //*************************************************************************** + /// Round to more negative integer. + /// \param value Scaled integral. + /// \return Unscaled, rounded integral. + //*************************************************************************** + template + T round_floor_unscaled(T value) + { + ETL_STATIC_ASSERT(etl::is_integral::value, "Type must be an integral"); + typedef typename scaled_rounding_t::type scale_t; + + if (value >= 0) + { + return T(value / scale_t(SCALING)); + } + else + { + return T((value - scale_t(SCALING)) / scale_t(SCALING)); + } + } + + //*************************************************************************** + /// Round to more negative integer. + /// \param value Scaled integral. + /// \return Scaled, rounded integral. + //*************************************************************************** + template + T round_floor_scaled(T value) + { + typedef typename scaled_rounding_t::type scale_t; + + return T(round_floor_unscaled(value) * scale_t(SCALING)); + } + + //*************************************************************************** + /// Round to nearest integer. 'Half' value is rounded up (to infinity). + /// Uses 'symmetric up' rounding. + /// \param value Scaled integral. + /// \return Unscaled, rounded integral. + //*************************************************************************** + template + T round_half_up_unscaled(T value) + { + ETL_STATIC_ASSERT(etl::is_integral::value, "Type must be an integral"); + ETL_STATIC_ASSERT((((SCALING / 2U) * 2U) == SCALING), "Scaling must be divisible by 2"); + typedef typename scaled_rounding_t::type scale_t; + + if (value >= 0) + { + return T((value + scale_t(SCALING / 2U)) / scale_t(SCALING)); + } + else + { + return T((value - scale_t(SCALING / 2U)) / scale_t(SCALING)); + } + } + + //*************************************************************************** + /// Round to nearest integer. 'Half' value is rounded up (to infinity). + /// Uses 'symmetric up' rounding. + /// \param value Scaled integral. + /// \return Scaled, rounded integral. + //*************************************************************************** + template + T round_half_up_scaled(T value) + { + typedef typename scaled_rounding_t::type scale_t; + + return T(round_half_up_unscaled(value) * scale_t(SCALING)); + } + + //*************************************************************************** + /// Round to nearest integer. 'Half' value is rounded down (to zero). + /// Uses 'symmetric down' rounding. + /// \param value Scaled integral. + /// \return Unscaled, rounded integral. + //*************************************************************************** + template + T round_half_down_unscaled(T value) + { + ETL_STATIC_ASSERT(etl::is_integral::value, "Type must be an integral"); + ETL_STATIC_ASSERT((((SCALING / 2U) * 2U) == SCALING), "Scaling must be divisible by 2"); + typedef typename scaled_rounding_t::type scale_t; + + if (value >= 0) + { + return T((value + scale_t((SCALING / 2U) - 1U)) / scale_t(SCALING)); + } + else + { + return T((value - scale_t((SCALING / 2U) - 1U)) / scale_t(SCALING)); + } + } + + //*************************************************************************** + /// Round to nearest integer. 'Half' value is rounded down (to zero). + /// Uses 'symmetric down' rounding. + /// \param value Scaled integral. + /// \return Scaled, rounded integral. + //*************************************************************************** + template + T round_half_down_scaled(T value) + { + typedef typename scaled_rounding_t::type scale_t; + + return T(round_half_down_unscaled(value) * scale_t(SCALING)); + } + + //*************************************************************************** + /// Round toward zero. + /// \param value Scaled integral. + /// \return Unscaled, rounded integral. + //*************************************************************************** + template + T round_zero_unscaled(T value) + { + ETL_STATIC_ASSERT(etl::is_integral::value, "Type must be an integral"); + typedef typename scaled_rounding_t::type scale_t; + + return T(value / scale_t(SCALING)); + } + + //*************************************************************************** + /// Round toward zero. + /// \param value Scaled integral. + /// \return Scaled, rounded integral. + //*************************************************************************** + template + T round_zero_scaled(T value) + { + typedef typename scaled_rounding_t::type scale_t; + + return T(round_zero_unscaled(value) * scale_t(SCALING)); + } + + //*************************************************************************** + /// Round toward infinity. + /// \param value Scaled integral. + /// \return Unscaled, rounded integral. + //*************************************************************************** + template + T round_infinity_unscaled(T value) + { + ETL_STATIC_ASSERT(etl::is_integral::value, "Type must be an integral"); + typedef typename scaled_rounding_t::type scale_t; + + if (value >= 0) + { + return T((value + scale_t(SCALING)) / scale_t(SCALING)); + } + else + { + return T((value - scale_t(SCALING)) / scale_t(SCALING)); + } + } + + //*************************************************************************** + /// Round toward infinity. + /// \param value Scaled integral. + /// \return Ccaled, rounded integral. + //*************************************************************************** + template + T round_infinity_scaled(T value) + { + typedef typename scaled_rounding_t::type scale_t; + + return T(round_infinity_unscaled(value) * scale_t(SCALING)); + } + + //*************************************************************************** + /// Round to nearest integer. 'Half' value is rounded to even integral. + /// Also known as 'Banker's Rounding'. + /// \param value Scaled integral. + /// \return Unscaled, rounded integral. + //*************************************************************************** + template + T round_half_even_unscaled(T value) + { + ETL_STATIC_ASSERT(etl::is_integral::value, "Type must be an integral"); + typedef typename scaled_rounding_t::type scale_t; + + // Half? + if ((etl::absolute(value) % scale_t(SCALING)) == scale_t(SCALING / 2U)) + { + // Odd? + if ((value / scale_t(SCALING)) & 1U) + { + return T(round_half_up_unscaled(value)); + } + else + { + return T(round_half_down_unscaled(value)); + } + } + else + { + return T(round_half_up_unscaled(value)); + } + } + + //*************************************************************************** + /// Round to nearest integer. 'Half' value is rounded to even integral. + /// Also known as 'Banker's Rounding'. + /// \param value Scaled integral. + /// \return Scaled, rounded integral. + //*************************************************************************** + template + T round_half_even_scaled(T value) + { + typedef typename scaled_rounding_t::type scale_t; + + return T(round_half_even_unscaled(value) * scale_t(SCALING)); + } + + //*************************************************************************** + /// Round to nearest integer. 'Half' value is rounded to odd integral. + /// Also known as 'Banker's Rounding'. + /// \param value Scaled integral. + /// \return Unscaled, rounded integral. + //*************************************************************************** + template + T round_half_odd_unscaled(T value) + { + ETL_STATIC_ASSERT(etl::is_integral::value, "Type must be an integral"); + typedef typename scaled_rounding_t::type scale_t; + + // Half? + if ((etl::absolute(value) % scale_t(SCALING)) == scale_t(SCALING / 2U)) + { + // Odd? + if ((value / scale_t(SCALING)) & 1U) + { + return T(round_half_down_unscaled(value)); + } + else + { + return T(round_half_up_unscaled(value)); + } + } + else + { + return T(round_half_up_unscaled(value)); + } + } + + //*************************************************************************** + /// Round to nearest integer. 'Half' value is rounded to odd integral. + /// Also known as 'Banker's Rounding'. + /// \param value Scaled integral. + /// \return Scaled, rounded integral. + //*************************************************************************** + template + T round_half_odd_scaled(T value) + { + typedef typename scaled_rounding_t::type scale_t; + + return T(round_half_odd_unscaled(value) * scale_t(SCALING)); + } +} + +#endif diff --git a/include/etl/version.h b/include/etl/version.h index cb340a46..5339759e 100644 --- a/include/etl/version.h +++ b/include/etl/version.h @@ -38,8 +38,8 @@ SOFTWARE. ///\ingroup utilities #define ETL_VERSION_MAJOR 14 -#define ETL_VERSION_MINOR 5 -#define ETL_VERSION_PATCH 1 +#define ETL_VERSION_MINOR 6 +#define ETL_VERSION_PATCH 0 #define ETL_VERSION ETL_STRINGIFY(ETL_VERSION_MAJOR) ETL_STRINGIFY(ETL_VERSION_MINOR) ETL_STRINGIFY(ETL_VERSION_PATCH) #define ETL_VERSION_W ETL_WIDE_STRING(ETL_CONCAT(ETL_CONCAT(ETL_VERSION_MAJOR, ETL_VERSION_MINOR), ETL_VERSION_PATCH)) diff --git a/support/Release notes.txt b/support/Release notes.txt index e2fa249f..873008fb 100644 --- a/support/Release notes.txt +++ b/support/Release notes.txt @@ -1,3 +1,14 @@ +=============================================================================== +14.6.0 +Added etl::scaled_rounding to allow selection of rounding algorithms when +emulating fixed point arithmetic with scaled integral values. + +Added etl::cumulating_moving_average, implementing an algorithm for +calculating an average for a stream of samples. There are specialisations +for floating point and scaled integral sample types. + +Added C++11 rvalue reference 'push' functions for etl::deque. + =============================================================================== 14.5.1 Fixed deque pushes for literals. diff --git a/test/codeblocks/ETL.cbp b/test/codeblocks/ETL.cbp index 9bed99f1..b07b8954 100644 --- a/test/codeblocks/ETL.cbp +++ b/test/codeblocks/ETL.cbp @@ -413,6 +413,7 @@ + diff --git a/test/test_cumulative_moving_average.cpp b/test/test_cumulative_moving_average.cpp new file mode 100644 index 00000000..ac277acc --- /dev/null +++ b/test/test_cumulative_moving_average.cpp @@ -0,0 +1,125 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "UnitTest++.h" + +#include "etl/cumulative_moving_average.h" +#include "etl/scaled_rounding.h" + +namespace +{ + const size_t SAMPLE_SIZE = 10U; + const size_t SCALING = 100U; + + SUITE(test_cumulative_moving_average) + { + //************************************************************************* + TEST(integral_signed_average_positive) + { + typedef etl::cumulative_moving_average CMA; + CMA cma(0); + + CHECK_EQUAL(0, cma.value()); + + cma.add(9); + cma.add(1); + cma.add(8); + cma.add(2); + cma.add(7); + cma.add(3); + cma.add(6); + cma.add(4); + cma.add(5); + + CHECK_EQUAL(280, cma.value()); + } + + //************************************************************************* + TEST(integral_signed_average_negative) + { + typedef etl::cumulative_moving_average CMA; + CMA cma(0); + + CHECK_EQUAL(0, cma.value()); + + cma.add(-9); + cma.add(-1); + cma.add(-8); + cma.add(-2); + cma.add(-7); + cma.add(-3); + cma.add(-6); + cma.add(-4); + cma.add(-5); + + CHECK_EQUAL(-280, cma.value()); + } + + //************************************************************************* + TEST(integral_unsigned_average_positive) + { + typedef etl::cumulative_moving_average CMA; + CMA cma(0U); + + CHECK_EQUAL(0U, cma.value()); + + cma.add(9U); + cma.add(1U); + cma.add(8U); + cma.add(2U); + cma.add(7U); + cma.add(3U); + cma.add(6U); + cma.add(4U); + cma.add(5U); + + CHECK_EQUAL(280U, cma.value()); + } + + //************************************************************************* + TEST(floating_point_average) + { + typedef etl::cumulative_moving_average CMA; + CMA cma(0); + + CHECK_EQUAL(0.0, cma.value()); + + cma.add(9.0); + cma.add(1.0); + cma.add(8.0); + cma.add(2.0); + cma.add(7.0); + cma.add(3.0); + cma.add(6.0); + cma.add(4.0); + cma.add(5.0); + + CHECK_CLOSE(2.82, cma.value(), 0.01); + } + }; +} diff --git a/test/test_scaled_rounding.cpp b/test/test_scaled_rounding.cpp new file mode 100644 index 00000000..25fb5036 --- /dev/null +++ b/test/test_scaled_rounding.cpp @@ -0,0 +1,345 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "UnitTest++.h" + +#include "etl/scaled_rounding.h" + +#include + +namespace +{ + std::array source = { 54, 55, 56, 64, 65, 66, -54, -55, -56, -64, -65, -66 }; + + SUITE(test_scaled_rounding) + { + //************************************************************************* + TEST(round_ceiling_scaled) + { + std::array expected = { 60, 60, 60, 70, 70, 70, -50, -50, -50, -60, -60, -60 }; + + CHECK_EQUAL(expected[0], etl::round_ceiling_scaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_ceiling_scaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_ceiling_scaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_ceiling_scaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_ceiling_scaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_ceiling_scaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_ceiling_scaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_ceiling_scaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_ceiling_scaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_ceiling_scaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_ceiling_scaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_ceiling_scaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_ceiling_unscaled) + { + std::array expected = { 6, 6, 6, 7, 7, 7, -5, -5, -5, -6, -6, -6 }; + + CHECK_EQUAL(expected[0], etl::round_ceiling_unscaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_ceiling_unscaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_ceiling_unscaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_ceiling_unscaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_ceiling_unscaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_ceiling_unscaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_ceiling_unscaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_ceiling_unscaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_ceiling_unscaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_ceiling_unscaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_ceiling_unscaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_ceiling_unscaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_floor_scaled) + { + std::array expected = { 50, 50, 50, 60, 60, 60, -60, -60, -60, -70, -70, -70 }; + + CHECK_EQUAL(expected[0], etl::round_floor_scaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_floor_scaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_floor_scaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_floor_scaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_floor_scaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_floor_scaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_floor_scaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_floor_scaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_floor_scaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_floor_scaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_floor_scaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_floor_scaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_floor_unscaled) + { + std::array expected = { 5, 5, 5, 6, 6, 6, -6, -6, -6, -7, -7, -7 }; + + CHECK_EQUAL(expected[0], etl::round_floor_unscaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_floor_unscaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_floor_unscaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_floor_unscaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_floor_unscaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_floor_unscaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_floor_unscaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_floor_unscaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_floor_unscaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_floor_unscaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_floor_unscaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_floor_unscaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_half_up_scaled) + { + std::array expected = { 50, 60, 60, 60, 70, 70, -50, -60, -60, -60, -70, -70 }; + + CHECK_EQUAL(expected[0], etl::round_half_up_scaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_half_up_scaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_half_up_scaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_half_up_scaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_half_up_scaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_half_up_scaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_half_up_scaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_half_up_scaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_half_up_scaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_half_up_scaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_half_up_scaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_half_up_scaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_half_up_unscaled) + { + std::array expected = { 5, 6, 6, 6, 7, 7, -5, -6, -6, -6, -7, -7 }; + + CHECK_EQUAL(expected[0], etl::round_half_up_unscaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_half_up_unscaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_half_up_unscaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_half_up_unscaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_half_up_unscaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_half_up_unscaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_half_up_unscaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_half_up_unscaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_half_up_unscaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_half_up_unscaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_half_up_unscaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_half_up_unscaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_half_down_scaled) + { + std::array expected = { 50, 50, 60, 60, 60, 70, -50, -50, -60, -60, -60, -70 }; + + CHECK_EQUAL(expected[0], etl::round_half_down_scaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_half_down_scaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_half_down_scaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_half_down_scaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_half_down_scaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_half_down_scaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_half_down_scaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_half_down_scaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_half_down_scaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_half_down_scaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_half_down_scaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_half_down_scaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_half_down_unscaled) + { + std::array expected = { 5, 5, 6, 6, 6, 7, -5, -5, -6, -6, -6, -7 }; + + CHECK_EQUAL(expected[0], etl::round_half_down_unscaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_half_down_unscaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_half_down_unscaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_half_down_unscaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_half_down_unscaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_half_down_unscaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_half_down_unscaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_half_down_unscaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_half_down_unscaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_half_down_unscaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_half_down_unscaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_half_down_unscaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_zero_scaled) + { + std::array expected = { 50, 50, 50, 60, 60, 60, -50, -50, -50, -60, -60, -60 }; + + CHECK_EQUAL(expected[0], etl::round_zero_scaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_zero_scaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_zero_scaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_zero_scaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_zero_scaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_zero_scaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_zero_scaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_zero_scaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_zero_scaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_zero_scaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_zero_scaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_zero_scaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_zero_unscaled) + { + std::array expected = { 5, 5, 5, 6, 6, 6, -5, -5, -5, -6, -6, -6 }; + + CHECK_EQUAL(expected[0], etl::round_zero_unscaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_zero_unscaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_zero_unscaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_zero_unscaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_zero_unscaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_zero_unscaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_zero_unscaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_zero_unscaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_zero_unscaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_zero_unscaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_zero_unscaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_zero_unscaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_infinity_scaled) + { + std::array expected = { 60, 60, 60, 70, 70, 70, -60, -60, -60, -70, -70, -70 }; + + CHECK_EQUAL(expected[0], etl::round_infinity_scaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_infinity_scaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_infinity_scaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_infinity_scaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_infinity_scaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_infinity_scaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_infinity_scaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_infinity_scaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_infinity_scaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_infinity_scaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_infinity_scaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_infinity_scaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_infinity_unscaled) + { + std::array expected = { 6, 6, 6, 7, 7, 7, -6, -6, -6, -7, -7, -7 }; + + CHECK_EQUAL(expected[0], etl::round_infinity_unscaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_infinity_unscaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_infinity_unscaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_infinity_unscaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_infinity_unscaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_infinity_unscaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_infinity_unscaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_infinity_unscaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_infinity_unscaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_infinity_unscaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_infinity_unscaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_infinity_unscaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_half_even_scaled) + { + std::array expected = { 50, 60, 60, 60, 60, 70, -50, -60, -60, -60, -60, -70 }; + + CHECK_EQUAL(expected[0], etl::round_half_even_scaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_half_even_scaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_half_even_scaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_half_even_scaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_half_even_scaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_half_even_scaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_half_even_scaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_half_even_scaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_half_even_scaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_half_even_scaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_half_even_scaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_half_even_scaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_half_even_unscaled) + { + std::array expected = { 5, 6, 6, 6, 6, 7, -5, -6, -6, -6, -6, -7 }; + + CHECK_EQUAL(expected[0], etl::round_half_even_unscaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_half_even_unscaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_half_even_unscaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_half_even_unscaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_half_even_unscaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_half_even_unscaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_half_even_unscaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_half_even_unscaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_half_even_unscaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_half_even_unscaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_half_even_unscaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_half_even_unscaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_half_odd_scaled) + { + std::array expected = { 50, 50, 60, 60, 70, 70, -50, -50, -60, -60, -70, -70 }; + + CHECK_EQUAL(expected[0], etl::round_half_odd_scaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_half_odd_scaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_half_odd_scaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_half_odd_scaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_half_odd_scaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_half_odd_scaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_half_odd_scaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_half_odd_scaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_half_odd_scaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_half_odd_scaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_half_odd_scaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_half_odd_scaled<10>(source[11])); + } + + //************************************************************************* + TEST(round_half_odd_unscaled) + { + std::array expected = { 5, 5, 6, 6, 7, 7, -5, -5, -6, -6, -7, -7 }; + + CHECK_EQUAL(expected[0], etl::round_half_odd_unscaled<10>(source[0])); + CHECK_EQUAL(expected[1], etl::round_half_odd_unscaled<10>(source[1])); + CHECK_EQUAL(expected[2], etl::round_half_odd_unscaled<10>(source[2])); + CHECK_EQUAL(expected[3], etl::round_half_odd_unscaled<10>(source[3])); + CHECK_EQUAL(expected[4], etl::round_half_odd_unscaled<10>(source[4])); + CHECK_EQUAL(expected[5], etl::round_half_odd_unscaled<10>(source[5])); + CHECK_EQUAL(expected[6], etl::round_half_odd_unscaled<10>(source[6])); + CHECK_EQUAL(expected[7], etl::round_half_odd_unscaled<10>(source[7])); + CHECK_EQUAL(expected[8], etl::round_half_odd_unscaled<10>(source[8])); + CHECK_EQUAL(expected[9], etl::round_half_odd_unscaled<10>(source[9])); + CHECK_EQUAL(expected[10], etl::round_half_odd_unscaled<10>(source[10])); + CHECK_EQUAL(expected[11], etl::round_half_odd_unscaled<10>(source[11])); + } + }; +} diff --git a/test/vs2017/etl.vcxproj b/test/vs2017/etl.vcxproj index 72650a15..961afff0 100644 --- a/test/vs2017/etl.vcxproj +++ b/test/vs2017/etl.vcxproj @@ -369,10 +369,13 @@ + + + @@ -561,6 +564,7 @@ + @@ -720,6 +724,7 @@ + true true diff --git a/test/vs2017/etl.vcxproj.filters b/test/vs2017/etl.vcxproj.filters index 5cf73014..84162b51 100644 --- a/test/vs2017/etl.vcxproj.filters +++ b/test/vs2017/etl.vcxproj.filters @@ -711,6 +711,15 @@ ETL\Maths + + ETL\Maths + + + ETL\Maths + + + ETL\Maths + @@ -1127,8 +1136,11 @@ Source Files - - ETL\Maths + + Source Files + + + Source Files