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