1/* Helper macros for functions returning a narrower type.
2 Copyright (C) 2018-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#ifndef _MATH_NARROW_H
20#define _MATH_NARROW_H 1
21
22#include <bits/floatn.h>
23#include <bits/long-double.h>
24#include <errno.h>
25#include <fenv.h>
26#include <ieee754.h>
27#include <math-barriers.h>
28#include <math_private.h>
29#include <fenv_private.h>
30#include <math-narrow-alias.h>
31#include <stdbool.h>
32
33/* Carry out a computation using round-to-odd. The computation is
34 EXPR; the union type in which to store the result is UNION and the
35 subfield of the "ieee" field of that union with the low part of the
36 mantissa is MANTISSA; SUFFIX is the suffix for both underlying libm
37 functions for the argument type (for computations where a libm
38 function rather than a C operator is used when argument and result
39 types are the same) and the libc_fe* macros to ensure that the
40 correct rounding mode is used, for platforms with multiple rounding
41 modes where those macros set only the relevant mode.
42 CLEAR_UNDERFLOW indicates whether underflow exceptions must be
43 cleared (in the case where a round-toward-zero underflow might not
44 indicate an underflow after narrowing, when that narrowing only
45 reduces precision not exponent range and the architecture uses
46 before-rounding tininess detection). This macro does not work
47 correctly if the sign of an exact zero result depends on the
48 rounding mode, so that case must be checked for separately. */
49#define ROUND_TO_ODD(EXPR, UNION, SUFFIX, MANTISSA, CLEAR_UNDERFLOW) \
50 ({ \
51 fenv_t env; \
52 UNION u; \
53 \
54 libc_feholdexcept_setround ## SUFFIX (&env, FE_TOWARDZERO); \
55 u.d = (EXPR); \
56 math_force_eval (u.d); \
57 if (CLEAR_UNDERFLOW) \
58 feclearexcept (FE_UNDERFLOW); \
59 u.ieee.MANTISSA \
60 |= libc_feupdateenv_test ## SUFFIX (&env, FE_INEXACT) != 0; \
61 \
62 u.d; \
63 })
64
65/* Check for error conditions from a narrowing add function returning
66 RET with arguments X and Y and set errno as needed. Overflow and
67 underflow can occur for finite arguments and a domain error for
68 infinite ones. */
69#define CHECK_NARROW_ADD(RET, X, Y) \
70 do \
71 { \
72 if (!isfinite (RET)) \
73 { \
74 if (isnan (RET)) \
75 { \
76 if (!isnan (X) && !isnan (Y)) \
77 __set_errno (EDOM); \
78 } \
79 else if (isfinite (X) && isfinite (Y)) \
80 __set_errno (ERANGE); \
81 } \
82 else if ((RET) == 0 && (X) != -(Y)) \
83 __set_errno (ERANGE); \
84 } \
85 while (0)
86
87/* Implement narrowing add using round-to-odd. The arguments are X
88 and Y, the return type is TYPE and UNION, MANTISSA and SUFFIX are
89 as for ROUND_TO_ODD. */
90#define NARROW_ADD_ROUND_TO_ODD(X, Y, TYPE, UNION, SUFFIX, MANTISSA) \
91 do \
92 { \
93 TYPE ret; \
94 \
95 /* Ensure a zero result is computed in the original rounding \
96 mode. */ \
97 if ((X) == -(Y)) \
98 ret = (TYPE) ((X) + (Y)); \
99 else \
100 ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) + (Y), \
101 UNION, SUFFIX, MANTISSA, false); \
102 \
103 CHECK_NARROW_ADD (ret, (X), (Y)); \
104 return ret; \
105 } \
106 while (0)
107
108/* Implement a narrowing add function that is not actually narrowing
109 or where no attempt is made to be correctly rounding (the latter
110 only applies to IBM long double). The arguments are X and Y and
111 the return type is TYPE. */
112#define NARROW_ADD_TRIVIAL(X, Y, TYPE) \
113 do \
114 { \
115 TYPE ret; \
116 \
117 ret = (TYPE) ((X) + (Y)); \
118 CHECK_NARROW_ADD (ret, (X), (Y)); \
119 return ret; \
120 } \
121 while (0)
122
123/* Check for error conditions from a narrowing subtract function
124 returning RET with arguments X and Y and set errno as needed.
125 Overflow and underflow can occur for finite arguments and a domain
126 error for infinite ones. */
127#define CHECK_NARROW_SUB(RET, X, Y) \
128 do \
129 { \
130 if (!isfinite (RET)) \
131 { \
132 if (isnan (RET)) \
133 { \
134 if (!isnan (X) && !isnan (Y)) \
135 __set_errno (EDOM); \
136 } \
137 else if (isfinite (X) && isfinite (Y)) \
138 __set_errno (ERANGE); \
139 } \
140 else if ((RET) == 0 && (X) != (Y)) \
141 __set_errno (ERANGE); \
142 } \
143 while (0)
144
145/* Implement narrowing subtract using round-to-odd. The arguments are
146 X and Y, the return type is TYPE and UNION, MANTISSA and SUFFIX are
147 as for ROUND_TO_ODD. */
148#define NARROW_SUB_ROUND_TO_ODD(X, Y, TYPE, UNION, SUFFIX, MANTISSA) \
149 do \
150 { \
151 TYPE ret; \
152 \
153 /* Ensure a zero result is computed in the original rounding \
154 mode. */ \
155 if ((X) == (Y)) \
156 ret = (TYPE) ((X) - (Y)); \
157 else \
158 ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) - (Y), \
159 UNION, SUFFIX, MANTISSA, false); \
160 \
161 CHECK_NARROW_SUB (ret, (X), (Y)); \
162 return ret; \
163 } \
164 while (0)
165
166/* Implement a narrowing subtract function that is not actually
167 narrowing or where no attempt is made to be correctly rounding (the
168 latter only applies to IBM long double). The arguments are X and Y
169 and the return type is TYPE. */
170#define NARROW_SUB_TRIVIAL(X, Y, TYPE) \
171 do \
172 { \
173 TYPE ret; \
174 \
175 ret = (TYPE) ((X) - (Y)); \
176 CHECK_NARROW_SUB (ret, (X), (Y)); \
177 return ret; \
178 } \
179 while (0)
180
181/* Check for error conditions from a narrowing multiply function
182 returning RET with arguments X and Y and set errno as needed.
183 Overflow and underflow can occur for finite arguments and a domain
184 error for Inf * 0. */
185#define CHECK_NARROW_MUL(RET, X, Y) \
186 do \
187 { \
188 if (!isfinite (RET)) \
189 { \
190 if (isnan (RET)) \
191 { \
192 if (!isnan (X) && !isnan (Y)) \
193 __set_errno (EDOM); \
194 } \
195 else if (isfinite (X) && isfinite (Y)) \
196 __set_errno (ERANGE); \
197 } \
198 else if ((RET) == 0 && (X) != 0 && (Y) != 0) \
199 __set_errno (ERANGE); \
200 } \
201 while (0)
202
203/* Implement narrowing multiply using round-to-odd. The arguments are
204 X and Y, the return type is TYPE and UNION, MANTISSA, SUFFIX and
205 CLEAR_UNDERFLOW are as for ROUND_TO_ODD. */
206#define NARROW_MUL_ROUND_TO_ODD(X, Y, TYPE, UNION, SUFFIX, MANTISSA, \
207 CLEAR_UNDERFLOW) \
208 do \
209 { \
210 TYPE ret; \
211 \
212 ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) * (Y), \
213 UNION, SUFFIX, MANTISSA, \
214 CLEAR_UNDERFLOW); \
215 \
216 CHECK_NARROW_MUL (ret, (X), (Y)); \
217 return ret; \
218 } \
219 while (0)
220
221/* Implement a narrowing multiply function that is not actually
222 narrowing or where no attempt is made to be correctly rounding (the
223 latter only applies to IBM long double). The arguments are X and Y
224 and the return type is TYPE. */
225#define NARROW_MUL_TRIVIAL(X, Y, TYPE) \
226 do \
227 { \
228 TYPE ret; \
229 \
230 ret = (TYPE) ((X) * (Y)); \
231 CHECK_NARROW_MUL (ret, (X), (Y)); \
232 return ret; \
233 } \
234 while (0)
235
236/* Check for error conditions from a narrowing divide function
237 returning RET with arguments X and Y and set errno as needed.
238 Overflow, underflow and divide-by-zero can occur for finite
239 arguments and a domain error for Inf / Inf and 0 / 0. */
240#define CHECK_NARROW_DIV(RET, X, Y) \
241 do \
242 { \
243 if (!isfinite (RET)) \
244 { \
245 if (isnan (RET)) \
246 { \
247 if (!isnan (X) && !isnan (Y)) \
248 __set_errno (EDOM); \
249 } \
250 else if (isfinite (X)) \
251 __set_errno (ERANGE); \
252 } \
253 else if ((RET) == 0 && (X) != 0 && !isinf (Y)) \
254 __set_errno (ERANGE); \
255 } \
256 while (0)
257
258/* Implement narrowing divide using round-to-odd. The arguments are X
259 and Y, the return type is TYPE and UNION, MANTISSA, SUFFIX and
260 CLEAR_UNDERFLOW are as for ROUND_TO_ODD. */
261#define NARROW_DIV_ROUND_TO_ODD(X, Y, TYPE, UNION, SUFFIX, MANTISSA, \
262 CLEAR_UNDERFLOW) \
263 do \
264 { \
265 TYPE ret; \
266 \
267 ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) / (Y), \
268 UNION, SUFFIX, MANTISSA, \
269 CLEAR_UNDERFLOW); \
270 \
271 CHECK_NARROW_DIV (ret, (X), (Y)); \
272 return ret; \
273 } \
274 while (0)
275
276/* Implement a narrowing divide function that is not actually
277 narrowing or where no attempt is made to be correctly rounding (the
278 latter only applies to IBM long double). The arguments are X and Y
279 and the return type is TYPE. */
280#define NARROW_DIV_TRIVIAL(X, Y, TYPE) \
281 do \
282 { \
283 TYPE ret; \
284 \
285 ret = (TYPE) ((X) / (Y)); \
286 CHECK_NARROW_DIV (ret, (X), (Y)); \
287 return ret; \
288 } \
289 while (0)
290
291/* Check for error conditions from a narrowing square root function
292 returning RET with argument X and set errno as needed. Overflow
293 and underflow can occur for finite positive arguments and a domain
294 error for negative arguments. */
295#define CHECK_NARROW_SQRT(RET, X) \
296 do \
297 { \
298 if (!isfinite (RET)) \
299 { \
300 if (isnan (RET)) \
301 { \
302 if (!isnan (X)) \
303 __set_errno (EDOM); \
304 } \
305 else if (isfinite (X)) \
306 __set_errno (ERANGE); \
307 } \
308 else if ((RET) == 0 && (X) != 0) \
309 __set_errno (ERANGE); \
310 } \
311 while (0)
312
313/* Implement narrowing square root using round-to-odd. The argument
314 is X, the return type is TYPE and UNION, MANTISSA and SUFFIX are as
315 for ROUND_TO_ODD. */
316#define NARROW_SQRT_ROUND_TO_ODD(X, TYPE, UNION, SUFFIX, MANTISSA) \
317 do \
318 { \
319 TYPE ret; \
320 \
321 ret = (TYPE) ROUND_TO_ODD (sqrt ## SUFFIX (math_opt_barrier (X)), \
322 UNION, SUFFIX, MANTISSA, false); \
323 \
324 CHECK_NARROW_SQRT (ret, (X)); \
325 return ret; \
326 } \
327 while (0)
328
329/* Implement a narrowing square root function where no attempt is made
330 to be correctly rounding (this only applies to IBM long double; the
331 case where the function is not actually narrowing is handled by
332 aliasing other sqrt functions in libm, not using this macro). The
333 argument is X and the return type is TYPE. */
334#define NARROW_SQRT_TRIVIAL(X, TYPE, SUFFIX) \
335 do \
336 { \
337 TYPE ret; \
338 \
339 ret = (TYPE) (sqrt ## SUFFIX (X)); \
340 CHECK_NARROW_SQRT (ret, (X)); \
341 return ret; \
342 } \
343 while (0)
344
345/* Check for error conditions from a narrowing fused multiply-add
346 function returning RET with arguments X, Y and Z and set errno as
347 needed. Checking for error conditions for fma (either narrowing or
348 not) and setting errno is not currently implemented. See bug
349 6801. */
350#define CHECK_NARROW_FMA(RET, X, Y, Z) \
351 do \
352 { \
353 } \
354 while (0)
355
356/* Implement narrowing fused multiply-add using round-to-odd. The
357 arguments are X, Y and Z, the return type is TYPE and UNION,
358 MANTISSA, SUFFIX and CLEAR_UNDERFLOW are as for ROUND_TO_ODD. */
359#define NARROW_FMA_ROUND_TO_ODD(X, Y, Z, TYPE, UNION, SUFFIX, MANTISSA, \
360 CLEAR_UNDERFLOW) \
361 do \
362 { \
363 typeof (X) tmp; \
364 TYPE ret; \
365 \
366 tmp = ROUND_TO_ODD (fma ## SUFFIX (math_opt_barrier (X), (Y), \
367 (Z)), \
368 UNION, SUFFIX, MANTISSA, CLEAR_UNDERFLOW); \
369 /* If the round-to-odd result is zero, the result is an exact \
370 zero and must be recomputed in the original rounding mode. */ \
371 if (tmp == 0) \
372 ret = (TYPE) (math_opt_barrier (X) * (Y) + (Z)); \
373 else \
374 ret = (TYPE) tmp; \
375 \
376 CHECK_NARROW_FMA (ret, (X), (Y), (Z)); \
377 return ret; \
378 } \
379 while (0)
380
381/* Implement a narrowing fused multiply-add function where no attempt
382 is made to be correctly rounding (this only applies to IBM long
383 double; the case where the function is not actually narrowing is
384 handled by aliasing other fma functions in libm, not using this
385 macro). The arguments are X, Y and Z and the return type is
386 TYPE. */
387#define NARROW_FMA_TRIVIAL(X, Y, Z, TYPE, SUFFIX) \
388 do \
389 { \
390 TYPE ret; \
391 \
392 ret = (TYPE) (fma ## SUFFIX ((X), (Y), (Z))); \
393 CHECK_NARROW_FMA (ret, (X), (Y), (Z)); \
394 return ret; \
395 } \
396 while (0)
397
398#endif /* math-narrow.h. */
399

source code of glibc/math/math-narrow.h