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