xref: /aosp_15_r20/external/ComputeLibrary/support/Rounding.h (revision c217d954acce2dbc11938adb493fc0abd69584f3)
1 /*
2  * Copyright (c) 2018-2021 Arm Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #ifndef ARM_COMPUTE_UTILS_ROUNDING_H
25 #define ARM_COMPUTE_UTILS_ROUNDING_H
26 
27 #include "arm_compute/core/Error.h"
28 #include "arm_compute/core/utils/misc/Traits.h"
29 #include "support/Requires.h"
30 #include "support/ToolchainSupport.h"
31 
32 #include <cmath>
33 
34 namespace arm_compute
35 {
36 namespace utils
37 {
38 namespace rounding
39 {
40 /** Rounding mode */
41 enum class RoundingMode
42 {
43     TO_ZERO,             /**< Round towards zero */
44     AWAY_FROM_ZERO,      /**< Round away from zero */
45     HALF_TO_ZERO,        /**< Round half towards from zero */
46     HALF_AWAY_FROM_ZERO, /**< Round half away from zero */
47     HALF_UP,             /**< Round half towards positive infinity */
48     HALF_DOWN,           /**< Round half towards negative infinity */
49     HALF_EVEN            /**< Round half towards nearest even */
50 };
51 
52 /** Round floating-point value with round to zero
53  *
54  * @tparam T Parameter type. Should be of floating point type.
55  *
56  * @param[in] value floating-point value to be rounded.
57  *
58  * @return Floating-point value of rounded @p value.
59  */
60 template <typename T, ARM_COMPUTE_REQUIRES_TA(traits::is_floating_point<T>::value)>
round_to_zero(T value)61 inline T round_to_zero(T value)
62 {
63     T res = std::floor(std::fabs(value));
64     return (value < 0.f) ? -res : res;
65 }
66 
67 /** Round floating-point value with round away from zero
68  *
69  * @tparam T Parameter type. Should be of floating point type.
70  *
71  * @param[in] value floating-point value to be rounded.
72  *
73  * @return Floating-point value of rounded @p value.
74  */
75 template <typename T, ARM_COMPUTE_REQUIRES_TA(traits::is_floating_point<T>::value)>
round_away_from_zero(T value)76 inline T round_away_from_zero(T value)
77 {
78     T res = std::ceil(std::fabs(value));
79     return (value < 0.f) ? -res : res;
80 }
81 
82 /** Round floating-point value with half value rounding towards zero.
83  *
84  * @tparam T Parameter type. Should be of floating point type.
85  *
86  * @param[in] value floating-point value to be rounded.
87  *
88  * @return Floating-point value of rounded @p value.
89  */
90 template <typename T, ARM_COMPUTE_REQUIRES_TA(traits::is_floating_point<T>::value)>
round_half_to_zero(T value)91 inline T round_half_to_zero(T value)
92 {
93     T res = T(std::ceil(std::fabs(value) - 0.5f));
94     return (value < 0.f) ? -res : res;
95 }
96 
97 /** Round floating-point value with half value rounding away from zero.
98  *
99  * @tparam T Parameter type. Should be of floating point type.
100  *
101  * @param[in] value floating-point value to be rounded.
102  *
103  * @return Floating-point value of rounded @p value.
104  */
105 template <typename T, ARM_COMPUTE_REQUIRES_TA(traits::is_floating_point<T>::value)>
round_half_away_from_zero(T value)106 inline T round_half_away_from_zero(T value)
107 {
108     T res = T(std::floor(std::fabs(value) + 0.5f));
109     return (value < 0.f) ? -res : res;
110 }
111 
112 /** Round floating-point value with half value rounding to positive infinity.
113  *
114  * @tparam T Parameter type. Should be of floating point type.
115  *
116  * @param[in] value floating-point value to be rounded.
117  *
118  * @return Floating-point value of rounded @p value.
119  */
120 template <typename T, ARM_COMPUTE_REQUIRES_TA(traits::is_floating_point<T>::value)>
round_half_up(T value)121 inline T round_half_up(T value)
122 {
123     return std::floor(value + 0.5f);
124 }
125 
126 /** Round floating-point value with half value rounding to negative infinity.
127  *
128  * @tparam T Parameter type. Should be of floating point type.
129  *
130  * @param[in] value floating-point value to be rounded.
131  *
132  * @return Floating-point value of rounded @p value.
133  */
134 template <typename T, ARM_COMPUTE_REQUIRES_TA(traits::is_floating_point<T>::value)>
round_half_down(T value)135 inline T round_half_down(T value)
136 {
137     return std::ceil(value - 0.5f);
138 }
139 
140 /** Round floating-point value with half value rounding to nearest even.
141  *
142  * @tparam T Parameter type. Should be of floating point type.
143  *
144  * @param[in] value   floating-point value to be rounded.
145  * @param[in] epsilon precision.
146  *
147  * @return Floating-point value of rounded @p value.
148  */
149 template <typename T, ARM_COMPUTE_REQUIRES_TA(traits::is_floating_point<T>::value)>
150 inline T round_half_even(T value, T epsilon = std::numeric_limits<T>::epsilon())
151 {
152     T positive_value = std::abs(value);
153     T ipart          = 0;
154     std::modf(positive_value, &ipart);
155     // If 'value' is exactly halfway between two integers
156     if(std::abs(positive_value - (ipart + 0.5f)) < epsilon)
157     {
158         // If 'ipart' is even then return 'ipart'
159         if(std::fmod(ipart, 2.f) < epsilon)
160         {
161             return support::cpp11::copysign(ipart, value);
162         }
163         // Else return the nearest even integer
164         return support::cpp11::copysign(std::ceil(ipart + 0.5f), value);
165     }
166     // Otherwise use the usual round to closest
167     return support::cpp11::copysign(support::cpp11::round(positive_value), value);
168 }
169 
170 /** Round floating-point value given a rounding mode
171  *
172  * @tparam T Parameter type. Should be of floating point type.
173  *
174  * @param[in] value         floating-point value to be rounded.
175  * @param[in] rounding_mode Rounding mode to use.
176  *
177  * @return Floating-point value of rounded @p value.
178  */
179 template <typename T, ARM_COMPUTE_REQUIRES_TA(traits::is_floating_point<T>::value)>
round(T value,RoundingMode rounding_mode)180 inline T round(T value, RoundingMode rounding_mode)
181 {
182     switch(rounding_mode)
183     {
184         case RoundingMode::TO_ZERO:
185             return round_to_zero(value);
186         case RoundingMode::AWAY_FROM_ZERO:
187             return round_away_from_zero(value);
188         case RoundingMode::HALF_TO_ZERO:
189             return round_half_to_zero(value);
190         case RoundingMode::HALF_AWAY_FROM_ZERO:
191             return round_half_away_from_zero(value);
192         case RoundingMode::HALF_UP:
193             return round_half_up(value);
194         case RoundingMode::HALF_DOWN:
195             return round_half_down(value);
196         case RoundingMode::HALF_EVEN:
197             return round_half_even(value);
198         default:
199             ARM_COMPUTE_ERROR("Unsupported rounding mode!");
200     }
201 }
202 } // namespace rounding
203 } // namespace utils
204 } // namespace arm_compute
205 #endif /*ARM_COMPUTE_UTILS_ROUNDING_H */
206