blob: fdbc3f0c06fe211728f9ab8a267ffc651ec99569 [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
2 * Copyright (c) 2017 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#include <cmath>
25#include <limits>
26
27namespace
28{
29template <typename TpIn, typename TpSat>
30inline TpSat saturate_convert(TpIn a)
31{
32 if(a > std::numeric_limits<TpSat>::max())
33 {
34 a = std::numeric_limits<TpSat>::max();
35 }
36 if(a < std::numeric_limits<TpSat>::min())
37 {
38 a = std::numeric_limits<TpSat>::min();
39 }
40 return static_cast<TpSat>(a);
41}
42} // namespace
43
44namespace arm_compute
45{
46inline qint8_t sqshl_qs8(qint8_t a, int shift)
47{
48 qint16_t tmp = static_cast<qint16_t>(a) << shift;
Michalis Spyrou0a8334c2017-06-14 18:00:05 +010049
Anthony Barbier6ff3b192017-09-04 18:44:23 +010050 // Saturate the result in case of overflow and cast to qint8_t
51 return saturate_convert<qint16_t, qint8_t>(tmp);
52}
53
Michalis Spyrou0a8334c2017-06-14 18:00:05 +010054inline qint16_t sqshl_qs16(qint16_t a, int shift)
55{
56 qint32_t tmp = static_cast<qint32_t>(a) << shift;
57
58 // Saturate the result in case of overflow and cast to qint16_t
59 return saturate_convert<qint32_t, qint16_t>(tmp);
60}
61
Anthony Barbier6ff3b192017-09-04 18:44:23 +010062inline qint8_t sabs_qs8(qint8_t a)
63{
Michalis Spyrou0a8334c2017-06-14 18:00:05 +010064 return (a < 0) ? (a == std::numeric_limits<int8_t>::min()) ? std::numeric_limits<int8_t>::max() : -a : a;
65}
66
67inline qint16_t sabs_qs16(qint16_t a)
68{
69 return (a < 0) ? (a == std::numeric_limits<int16_t>::min()) ? std::numeric_limits<int16_t>::max() : -a : a;
Anthony Barbier6ff3b192017-09-04 18:44:23 +010070}
71
72inline qint8_t sadd_qs8(qint8_t a, qint8_t b)
73{
74 return a + b;
75}
76
Michalis Spyrou0a8334c2017-06-14 18:00:05 +010077inline qint16_t sadd_qs16(qint16_t a, qint16_t b)
78{
79 return a + b;
80}
81
Anthony Barbier6ff3b192017-09-04 18:44:23 +010082inline qint8_t sqadd_qs8(qint8_t a, qint8_t b)
83{
84 // We need to store the temporary result in qint16_t otherwise we cannot evaluate the overflow
85 qint16_t tmp = (static_cast<qint16_t>(a) + static_cast<qint16_t>(b));
86
87 // Saturate the result in case of overflow and cast to qint8_t
88 return saturate_convert<qint16_t, qint8_t>(tmp);
89}
90
91inline qint16_t sqadd_qs16(qint16_t a, qint16_t b)
92{
93 // We need to store the temporary result in qint16_t otherwise we cannot evaluate the overflow
94 qint32_t tmp = (static_cast<qint32_t>(a) + static_cast<qint32_t>(b));
95
96 // Saturate the result in case of overflow and cast to qint16_t
97 return saturate_convert<qint32_t, qint16_t>(tmp);
98}
99
100inline qint8_t ssub_qs8(qint8_t a, qint8_t b)
101{
102 return a - b;
103}
104
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100105inline qint16_t ssub_qs16(qint16_t a, qint16_t b)
106{
107 return a - b;
108}
109
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100110inline qint8_t sqsub_qs8(qint8_t a, qint8_t b)
111{
112 // We need to store the temporary result in uint16_t otherwise we cannot evaluate the overflow
113 qint16_t tmp = static_cast<qint16_t>(a) - static_cast<qint16_t>(b);
114
115 // Saturate the result in case of overflow and cast to qint8_t
116 return saturate_convert<qint16_t, qint8_t>(tmp);
117}
118
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100119inline qint16_t sqsub_qs16(qint16_t a, qint16_t b)
120{
121 // We need to store the temporary result in qint32_t otherwise we cannot evaluate the overflow
122 qint32_t tmp = static_cast<qint32_t>(a) - static_cast<qint32_t>(b);
123
124 // Saturate the result in case of overflow and cast to qint16_t
125 return saturate_convert<qint32_t, qint16_t>(tmp);
126}
127
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100128inline qint8_t smul_qs8(qint8_t a, qint8_t b, int fixed_point_position)
129{
130 const qint16_t round_up_const = (1 << (fixed_point_position - 1));
131
132 qint16_t tmp = static_cast<qint16_t>(a) * static_cast<qint16_t>(b);
133
134 // Rounding up
135 tmp += round_up_const;
136
137 return static_cast<qint8_t>(tmp >> fixed_point_position);
138}
139
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100140inline qint16_t smul_qs16(qint16_t a, qint16_t b, int fixed_point_position)
141{
142 const qint32_t round_up_const = (1 << (fixed_point_position - 1));
143
144 qint32_t tmp = static_cast<qint32_t>(a) * static_cast<qint32_t>(b);
145
146 // Rounding up
147 tmp += round_up_const;
148
149 return static_cast<qint16_t>(tmp >> fixed_point_position);
150}
151
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100152inline qint8_t sqmul_qs8(qint8_t a, qint8_t b, int fixed_point_position)
153{
154 const qint16_t round_up_const = (1 << (fixed_point_position - 1));
155
156 qint16_t tmp = static_cast<qint16_t>(a) * static_cast<qint16_t>(b);
157
158 // Rounding up
159 tmp += round_up_const;
160
161 return saturate_convert<qint16_t, qint8_t>(tmp >> fixed_point_position);
162}
163
164inline qint16_t sqmul_qs16(qint16_t a, qint16_t b, int fixed_point_position)
165{
166 const qint32_t round_up_const = (1 << (fixed_point_position - 1));
167
168 qint32_t tmp = static_cast<qint32_t>(a) * static_cast<qint32_t>(b);
169
170 // Rounding up
171 tmp += round_up_const;
172
173 return saturate_convert<qint32_t, qint16_t>(tmp >> fixed_point_position);
174}
175
176inline qint16_t sqmull_qs8(qint8_t a, qint8_t b, int fixed_point_position)
177{
178 const qint16_t round_up_const = (1 << (fixed_point_position - 1));
179
180 qint16_t tmp = static_cast<qint16_t>(a) * static_cast<qint16_t>(b);
181
182 // Rounding up
183 tmp += round_up_const;
184
185 return tmp >> fixed_point_position;
186}
187
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100188inline qint32_t sqmull_qs16(qint16_t a, qint16_t b, int fixed_point_position)
189{
190 const qint32_t round_up_const = (1 << (fixed_point_position - 1));
191
192 qint32_t tmp = static_cast<qint32_t>(a) * static_cast<qint32_t>(b);
193
194 // Rounding up
195 tmp += round_up_const;
196
197 return tmp >> fixed_point_position;
198}
199
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100200inline qint8_t sinvsqrt_qs8(qint8_t a, int fixed_point_position)
201{
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100202 const qint8_t shift = 8 - (fixed_point_position + (__builtin_clz(a) - 24));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100203
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100204 const qint8_t const_three = (3 << fixed_point_position);
205 qint8_t temp = shift < 0 ? (a << -shift) : (a >> shift);
206 qint8_t x2 = temp;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100207
208 // We need three iterations to find the result
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100209 for(int i = 0; i < 3; ++i)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100210 {
211 qint8_t three_minus_dx = ssub_qs8(const_three, smul_qs8(temp, smul_qs8(x2, x2, fixed_point_position), fixed_point_position));
212 x2 = (smul_qs8(x2, three_minus_dx, fixed_point_position) >> 1);
213 }
214
215 temp = shift < 0 ? (x2 << (-shift >> 1)) : (x2 >> (shift >> 1));
216
217 return temp;
218}
219
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100220inline qint16_t sinvsqrt_qs16(qint16_t a, int fixed_point_position)
221{
222 const qint16_t shift = 16 - (fixed_point_position + (__builtin_clz(a) - 16));
223
224 const qint16_t const_three = (3 << fixed_point_position);
225 qint16_t temp = shift < 0 ? (a << -shift) : (a >> shift);
226 qint16_t x2 = temp;
227
228 // We need three iterations to find the result
229 for(int i = 0; i < 3; ++i)
230 {
231 qint16_t three_minus_dx = ssub_qs16(const_three, smul_qs16(temp, smul_qs16(x2, x2, fixed_point_position), fixed_point_position));
232 x2 = smul_qs16(x2, three_minus_dx, fixed_point_position) >> 1;
233 }
234
235 temp = shift < 0 ? (x2 << ((-shift) >> 1)) : (x2 >> (shift >> 1));
236
237 return temp;
238}
239
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100240inline qint8_t sdiv_qs8(qint8_t a, qint8_t b, int fixed_point_position)
241{
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100242 const qint16_t temp = a << fixed_point_position;
243 return static_cast<qint8_t>(temp / b);
244}
245
246inline qint16_t sdiv_qs16(qint16_t a, qint16_t b, int fixed_point_position)
247{
248 const qint32_t temp = a << fixed_point_position;
249 return static_cast<qint16_t>(temp / b);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100250}
251
252inline qint8_t sqexp_qs8(qint8_t a, int fixed_point_position)
253{
254 // Constants
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100255 const qint8_t const_one = (1 << fixed_point_position);
256 const qint8_t ln2 = ((0x58 >> (6 - fixed_point_position)) + 1) >> 1;
257 const qint8_t inv_ln2 = (((0x38 >> (6 - fixed_point_position)) + 1) >> 1) | const_one;
258 const qint8_t A = ((0x7F >> (6 - fixed_point_position)) + 1) >> 1;
259 const qint8_t B = ((0x3F >> (6 - fixed_point_position)) + 1) >> 1;
260 const qint8_t C = ((0x16 >> (6 - fixed_point_position)) + 1) >> 1;
261 const qint8_t D = ((0x05 >> (6 - fixed_point_position)) + 1) >> 1;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100262
263 // Polynomial expansion
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100264 const int dec_a = (sqmul_qs8(a, inv_ln2, fixed_point_position) >> fixed_point_position);
265 const qint8_t alpha = sabs_qs8(sqsub_qs8(a, sqmul_qs8(ln2, sqshl_qs8(dec_a, fixed_point_position), fixed_point_position)));
266 qint8_t sum = sqadd_qs8(sqmul_qs8(alpha, D, fixed_point_position), C);
267 sum = sqadd_qs8(sqmul_qs8(alpha, sum, fixed_point_position), B);
268 sum = sqadd_qs8(sqmul_qs8(alpha, sum, fixed_point_position), A);
269 sum = sqmul_qs8(alpha, sum, fixed_point_position);
270 sum = sqadd_qs8(sum, const_one);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100271
272 return (dec_a < 0) ? (sum >> -dec_a) : sqshl_qs8(sum, dec_a);
273}
274
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100275inline qint16_t sqexp_qs16(qint16_t a, int fixed_point_position)
276{
277 // Constants
278 const qint16_t const_one = (1 << fixed_point_position);
279 const qint16_t ln2 = ((0x58B9 >> (14 - fixed_point_position)) + 1) >> 1;
280 const qint16_t inv_ln2 = (((0x38AA >> (14 - fixed_point_position)) + 1) >> 1) | const_one;
281 const qint16_t A = ((0x7FBA >> (14 - fixed_point_position)) + 1) >> 1;
282 const qint16_t B = ((0x3FE9 >> (14 - fixed_point_position)) + 1) >> 1;
283 const qint16_t C = ((0x1693 >> (14 - fixed_point_position)) + 1) >> 1;
284 const qint16_t D = ((0x0592 >> (14 - fixed_point_position)) + 1) >> 1;
285
286 // Polynomial expansion
287 const int dec_a = (sqmul_qs16(a, inv_ln2, fixed_point_position) >> fixed_point_position);
288 const qint16_t alpha = sabs_qs16(sqsub_qs16(a, sqmul_qs16(ln2, sqshl_qs16(dec_a, fixed_point_position), fixed_point_position)));
289 qint16_t sum = sqadd_qs16(sqmul_qs16(alpha, D, fixed_point_position), C);
290 sum = sqadd_qs16(sqmul_qs16(alpha, sum, fixed_point_position), B);
291 sum = sqadd_qs16(sqmul_qs16(alpha, sum, fixed_point_position), A);
292 sum = sqmul_qs16(alpha, sum, fixed_point_position);
293 sum = sqadd_qs16(sum, const_one);
294
295 return (dec_a < 0) ? (sum >> -dec_a) : sqshl_qs16(sum, dec_a);
296}
297
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100298inline qint8_t slog_qs8(qint8_t a, int fixed_point_position)
299{
300 // Constants
301 qint8_t const_one = (1 << fixed_point_position);
302 qint8_t ln2 = (0x58 >> (7 - fixed_point_position));
303 qint8_t A = (0x5C >> (7 - fixed_point_position - 1));
304 qint8_t B = -(0x56 >> (7 - fixed_point_position));
305 qint8_t C = (0x29 >> (7 - fixed_point_position));
306 qint8_t D = -(0x0A >> (7 - fixed_point_position));
307
308 if((const_one == a) || (a < 0))
309 {
310 return 0;
311 }
312 else if(a < const_one)
313 {
314 return -slog_qs8(sdiv_qs8(const_one, a, fixed_point_position), fixed_point_position);
315 }
316
317 // Remove even powers of 2
318 qint8_t shift_val = 31 - __builtin_clz(a >> fixed_point_position);
319 a >>= shift_val;
320 a = ssub_qs8(a, const_one);
321
322 // Polynomial expansion
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100323 qint8_t sum = sqadd_qs8(sqmul_qs8(a, D, fixed_point_position), C);
324 sum = sqadd_qs8(sqmul_qs8(a, sum, fixed_point_position), B);
325 sum = sqadd_qs8(sqmul_qs8(a, sum, fixed_point_position), A);
326 sum = sqmul_qs8(a, sum, fixed_point_position);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100327
328 return smul_qs8(sadd_qs8(sum, shift_val << fixed_point_position), ln2, fixed_point_position);
329}
330
Michalis Spyrou0a8334c2017-06-14 18:00:05 +0100331inline qint16_t slog_qs16(qint16_t a, int fixed_point_position)
332{
333 // Constants
334 qint16_t const_one = (1 << fixed_point_position);
335 qint16_t ln2 = (0x58B9 >> (7 - fixed_point_position));
336 qint16_t A = (0x5C0F >> (7 - fixed_point_position - 1));
337 qint16_t B = -(0x56AE >> (7 - fixed_point_position));
338 qint16_t C = (0x2933 >> (7 - fixed_point_position));
339 qint16_t D = -(0x0AA7 >> (7 - fixed_point_position));
340
341 if((const_one == a) || (a < 0))
342 {
343 return 0;
344 }
345 else if(a < const_one)
346 {
347 return -slog_qs16(sdiv_qs16(const_one, a, fixed_point_position), fixed_point_position);
348 }
349
350 // Remove even powers of 2
351 qint16_t shift_val = 31 - __builtin_clz(a >> fixed_point_position);
352 a >>= shift_val;
353 a = ssub_qs16(a, const_one);
354
355 // Polynomial expansion
356 qint16_t sum = sqadd_qs16(sqmul_qs16(a, D, fixed_point_position), C);
357 sum = sqadd_qs16(sqmul_qs16(a, sum, fixed_point_position), B);
358 sum = sqadd_qs16(sqmul_qs16(a, sum, fixed_point_position), A);
359 sum = sqmul_qs16(a, sum, fixed_point_position);
360
361 return smul_qs16(sadd_qs16(sum, shift_val << fixed_point_position), ln2, fixed_point_position);
362}
363
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100364inline float scvt_f32_qs8(qint8_t a, int fixed_point_position)
365{
366 return static_cast<float>(a) / (1 << fixed_point_position);
367}
368
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100369inline qint8_t sqcvt_qs8_f32(float a, int fixed_point_position)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100370{
371 // round_nearest_integer(a * 2^(fixed_point_position))
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100372 return saturate_convert<float, qint8_t>(a * (1 << fixed_point_position) + ((a >= 0) ? 0.5 : -0.5));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100373}
374
375inline float scvt_f32_qs16(qint16_t a, int fixed_point_position)
376{
377 return static_cast<float>(a) / (1 << fixed_point_position);
378}
379
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100380inline qint16_t sqcvt_qs16_f32(float a, int fixed_point_position)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100381{
382 // round_nearest_integer(a * 2^(fixed_point_position))
Georgios Pinitas21efeb42017-07-04 12:47:17 +0100383 return saturate_convert<float, qint16_t>(a * (1 << fixed_point_position) + ((a >= 0) ? 0.5 : -0.5));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100384}
385
386inline qint8_t sqmovn_qs16(qint16_t a)
387{
388 // Saturate the result in case of overflow and cast to qint8_t
389 return saturate_convert<qint16_t, qint8_t>(a);
390}
391}