blob: 650137a4733d27d350780a8722e2873ecc671f5d [file] [log] [blame]
Georgios Pinitas303f0db2018-11-19 11:56:51 +00001/*
Michalis Spyrouf4643372019-11-29 16:17:13 +00002 * Copyright (c) 2018-2019 ARM Limited.
Georgios Pinitas303f0db2018-11-19 11:56:51 +00003 *
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 */
Michalis Spyrouf4643372019-11-29 16:17:13 +000024#ifndef ARM_COMPUTE_UTILS_ROUNDING_H
25#define ARM_COMPUTE_UTILS_ROUNDING_H
Georgios Pinitas303f0db2018-11-19 11:56:51 +000026
27#include "arm_compute/core/Error.h"
28#include "arm_compute/core/utils/misc/Requires.h"
29#include "arm_compute/core/utils/misc/Traits.h"
30#include "support/ToolchainSupport.h"
31
32#include <cmath>
33
34namespace arm_compute
35{
36namespace utils
37{
38namespace rounding
39{
40/** Rounding mode */
41enum 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 */
60template <typename T, REQUIRES_TA(traits::is_floating_point<T>::value)>
61inline 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 */
75template <typename T, REQUIRES_TA(traits::is_floating_point<T>::value)>
76inline 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 */
90template <typename T, REQUIRES_TA(traits::is_floating_point<T>::value)>
91inline 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 */
105template <typename T, REQUIRES_TA(traits::is_floating_point<T>::value)>
106inline 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 */
120template <typename T, REQUIRES_TA(traits::is_floating_point<T>::value)>
121inline 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 */
134template <typename T, REQUIRES_TA(traits::is_floating_point<T>::value)>
135inline 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 */
149template <typename T, REQUIRES_TA(traits::is_floating_point<T>::value)>
150inline 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 */
179template <typename T, REQUIRES_TA(traits::is_floating_point<T>::value)>
180inline 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
Michalis Spyrouf4643372019-11-29 16:17:13 +0000205#endif /*ARM_COMPUTE_UTILS_ROUNDING_H */