1 /* Implementation of the degree trignometric functions COSD, SIND, TAND.
2 Copyright (C) 2020-2024 Free Software Foundation, Inc.
3 Contributed by Steven G. Kargl <kargl@gcc.gnu.org>
4 and Fritz Reese <foreese@gcc.gnu.org>
6 This file is part of the GNU Fortran runtime library (libgfortran).
8 Libgfortran is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public
10 License as published by the Free Software Foundation; either
11 version 3 of the License, or (at your option) any later version.
13 Libgfortran is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. */
30 This file is included from both the FE and the runtime library code.
31 Operations are generalized using GMP/MPFR functions. When included from
32 libgfortran, these should be overridden using macros which will use native
33 operations conforming to the same API. From the FE, the GMP/MPFR functions can
36 The following macros are used and must be defined, unless listed as [optional]:
39 Type name for the real-valued parameter.
40 Variables of this type are constructed/destroyed using mpfr_init()
44 Return type of the functions.
47 Insert code to return a value.
48 The parameter x is the result variable, which was also the input parameter.
51 Type name for integer types.
54 Names for the degree-valued trig functions defined by this module.
56 ENABLE_SIND, ENABLE_COSD, ENABLE_TAND
57 Whether the degree-valued trig functions can be enabled.
60 If ENABLE_<xxx>D is not defined, this is substituted to assert an
61 error condition for function f, kind k, and parameter x.
62 The function argument is one of {sind, cosd, tand}.
65 Whether x is a regular number or zero (not inf or NaN).
68 Convert x from radians to degrees.
71 Set x to COSD(30), or equivalently, SIND(60).
73 TINY_LITERAL [optional]
74 Value subtracted from 1 to cause raise INEXACT for COSD(x) for x << 1.
75 If not set, COSD(x) for x <= COSD_SMALL_LITERAL simply returns 1.
77 COSD_SMALL_LITERAL [optional]
78 Value such that x <= COSD_SMALL_LITERAL implies COSD(x) = 1 to within the
79 precision of FTYPE. If not set, this condition is not checked.
81 SIND_SMALL_LITERAL [optional]
82 Value such that x <= SIND_SMALL_LITERAL implies SIND(x) = D2R(x) to within
83 the precision of FTYPE. If not set, this condition is not checked.
89 /* Compute sind(x) = sin(x * pi / 180). */
99 /* sin(-x) = - sin(x). */
101 mpfr_init_set_ui (one, 1, GFC_RND_MODE);
102 mpfr_copysign (s, one, x, GFC_RND_MODE);
105 #ifdef SIND_SMALL_LITERAL
106 /* sin(x) = x as x -> 0; but only for some precisions. */
109 mpfr_abs (ax, x, GFC_RND_MODE);
110 if (mpfr_cmp_ld (ax, SIND_SMALL_LITERAL) < 0)
121 mpfr_abs (x, x, GFC_RND_MODE);
122 #endif /* SIND_SMALL_LITERAL */
124 /* Reduce angle to x in [0,360]. */
126 mpfr_init_set_ui (period, 360, GFC_RND_MODE);
127 mpfr_fmod (x, x, period, GFC_RND_MODE);
130 /* Special cases with exact results. */
133 if (mpfr_get_z (n, x, GFC_RND_MODE) == 0 && mpz_divisible_ui_p (n, 30))
135 /* Flip sign for odd n*pi (x is % 360 so this is only for 180).
136 This respects sgn(sin(x)) = sgn(d/dx sin(x)) = sgn(cos(x)). */
137 if (mpz_divisible_ui_p (n, 180))
139 mpfr_set_ui (x, 0, GFC_RND_MODE);
140 if (mpz_cmp_ui (n, 180) == 0)
141 mpfr_neg (s, s, GFC_RND_MODE);
143 else if (mpz_divisible_ui_p (n, 90))
144 mpfr_set_si (x, (mpz_cmp_ui (n, 90) == 0 ? 1 : -1), GFC_RND_MODE);
145 else if (mpz_divisible_ui_p (n, 60))
148 if (mpz_cmp_ui (n, 180) >= 0)
149 mpfr_neg (x, x, GFC_RND_MODE);
152 mpfr_set_ld (x, (mpz_cmp_ui (n, 180) < 0 ? 0.5L : -0.5L),
156 /* Fold [0,360] into the range [0,45], and compute either SIN() or
157 COS() depending on symmetry of shifting into the [0,45] range. */
160 bool fold_cos = false;
161 if (mpfr_cmp_ui (x, 180) <= 0)
163 if (mpfr_cmp_ui (x, 90) <= 0)
165 if (mpfr_cmp_ui (x, 45) > 0)
167 /* x = COS(D2R(90 - x)) */
168 mpfr_ui_sub (x, 90, x, GFC_RND_MODE);
174 if (mpfr_cmp_ui (x, 135) <= 0)
176 mpfr_sub_ui (x, x, 90, GFC_RND_MODE);
180 mpfr_ui_sub (x, 180, x, GFC_RND_MODE);
184 else if (mpfr_cmp_ui (x, 270) <= 0)
186 if (mpfr_cmp_ui (x, 225) <= 0)
187 mpfr_sub_ui (x, x, 180, GFC_RND_MODE);
190 mpfr_ui_sub (x, 270, x, GFC_RND_MODE);
193 mpfr_neg (s, s, GFC_RND_MODE);
198 if (mpfr_cmp_ui (x, 315) <= 0)
200 mpfr_sub_ui (x, x, 270, GFC_RND_MODE);
204 mpfr_ui_sub (x, 360, x, GFC_RND_MODE);
205 mpfr_neg (s, s, GFC_RND_MODE);
211 mpfr_cos (x, x, GFC_RND_MODE);
213 mpfr_sin (x, x, GFC_RND_MODE);
216 mpfr_mul (x, x, s, GFC_RND_MODE);
221 /* Return NaN for +-Inf and NaN and raise exception. */
223 mpfr_sub (x, x, x, GFC_RND_MODE);
228 ERROR_RETURN(sind, KIND, x);
229 #endif // ENABLE_SIND
235 /* Compute cosd(x) = cos(x * pi / 180). */
241 #if defined(TINY_LITERAL) && defined(COSD_SMALL_LITERAL)
242 static const volatile FTYPE tiny = TINY_LITERAL;
247 #ifdef COSD_SMALL_LITERAL
251 mpfr_abs (ax, x, GFC_RND_MODE);
252 /* No spurious underflows!. In radians, cos(x) = 1-x*x/2 as x -> 0. */
253 if (mpfr_cmp_ld (ax, COSD_SMALL_LITERAL) <= 0)
255 mpfr_set_ui (x, 1, GFC_RND_MODE);
258 if (!mpfr_zero_p (ax))
259 mpfr_sub_d (x, x, tiny, GFC_RND_MODE);
269 mpfr_abs (x, x, GFC_RND_MODE);
270 #endif /* COSD_SMALL_LITERAL */
272 /* Reduce angle to ax in [0,360]. */
274 mpfr_init_set_ui (period, 360, GFC_RND_MODE);
275 mpfr_fmod (x, x, period, GFC_RND_MODE);
278 /* Special cases with exact results.
279 Return negative zero for cosd(270) for consistency with libm cos(). */
282 if (mpfr_get_z (n, x, GFC_RND_MODE) == 0 && mpz_divisible_ui_p (n, 30))
284 if (mpz_divisible_ui_p (n, 180))
285 mpfr_set_si (x, (mpz_cmp_ui (n, 180) == 0 ? -1 : 1),
287 else if (mpz_divisible_ui_p (n, 90))
288 mpfr_set_zero (x, 0);
289 else if (mpz_divisible_ui_p (n, 60))
291 mpfr_set_ld (x, 0.5, GFC_RND_MODE);
292 if (mpz_cmp_ui (n, 60) != 0 && mpz_cmp_ui (n, 300) != 0)
293 mpfr_neg (x, x, GFC_RND_MODE);
298 if (mpz_cmp_ui (n, 30) != 0 && mpz_cmp_ui (n, 330) != 0)
299 mpfr_neg (x, x, GFC_RND_MODE);
303 /* Fold [0,360] into the range [0,45], and compute either SIN() or
304 COS() depending on symmetry of shifting into the [0,45] range. */
308 bool fold_sin = false;
309 if (mpfr_cmp_ui (x, 180) <= 0)
311 if (mpfr_cmp_ui (x, 90) <= 0)
313 if (mpfr_cmp_ui (x, 45) > 0)
315 mpfr_ui_sub (x, 90, x, GFC_RND_MODE);
321 if (mpfr_cmp_ui (x, 135) <= 0)
323 mpfr_sub_ui (x, x, 90, GFC_RND_MODE);
327 mpfr_ui_sub (x, 180, x, GFC_RND_MODE);
332 else if (mpfr_cmp_ui (x, 270) <= 0)
334 if (mpfr_cmp_ui (x, 225) <= 0)
335 mpfr_sub_ui (x, x, 180, GFC_RND_MODE);
338 mpfr_ui_sub (x, 270, x, GFC_RND_MODE);
346 if (mpfr_cmp_ui (x, 315) <= 0)
348 mpfr_sub_ui (x, x, 270, GFC_RND_MODE);
352 mpfr_ui_sub (x, 360, x, GFC_RND_MODE);
358 mpfr_sin (x, x, GFC_RND_MODE);
360 mpfr_cos (x, x, GFC_RND_MODE);
363 mpfr_neg (x, x, GFC_RND_MODE);
369 /* Return NaN for +-Inf and NaN and raise exception. */
371 mpfr_sub (x, x, x, GFC_RND_MODE);
376 ERROR_RETURN(cosd, KIND, x);
377 #endif // ENABLE_COSD
383 /* Compute tand(x) = tan(x * pi / 180). */
393 /* tan(-x) = - tan(x). */
395 mpfr_init_set_ui (one, 1, GFC_RND_MODE);
396 mpfr_copysign (s, one, x, GFC_RND_MODE);
399 #ifdef SIND_SMALL_LITERAL
400 /* tan(x) = x as x -> 0; but only for some precisions. */
403 mpfr_abs (ax, x, GFC_RND_MODE);
404 if (mpfr_cmp_ld (ax, SIND_SMALL_LITERAL) < 0)
415 mpfr_abs (x, x, GFC_RND_MODE);
416 #endif /* SIND_SMALL_LITERAL */
418 /* Reduce angle to x in [0,360]. */
420 mpfr_init_set_ui (period, 360, GFC_RND_MODE);
421 mpfr_fmod (x, x, period, GFC_RND_MODE);
424 /* Special cases with exact results. */
427 if (mpfr_get_z (n, x, GFC_RND_MODE) == 0 && mpz_divisible_ui_p (n, 45))
429 if (mpz_divisible_ui_p (n, 180))
430 mpfr_set_zero (x, 0);
432 /* Though mathematically NaN is more appropriate for tan(n*90),
433 returning +/-Inf offers the advantage that 1/tan(n*90) returns 0,
434 which is mathematically sound. In fact we rely on this behavior
435 to implement COTAND(x) = 1 / TAND(x).
437 else if (mpz_divisible_ui_p (n, 90))
438 mpfr_set_inf (x, mpz_cmp_ui (n, 90) == 0 ? 0 : 1);
442 mpfr_set_ui (x, 1, GFC_RND_MODE);
443 if (mpz_cmp_ui (n, 45) != 0 && mpz_cmp_ui (n, 225) != 0)
444 mpfr_neg (x, x, GFC_RND_MODE);
450 /* Fold [0,360] into the range [0,90], and compute TAN(). */
451 if (mpfr_cmp_ui (x, 180) <= 0)
453 if (mpfr_cmp_ui (x, 90) > 0)
455 mpfr_ui_sub (x, 180, x, GFC_RND_MODE);
456 mpfr_neg (s, s, GFC_RND_MODE);
461 if (mpfr_cmp_ui (x, 270) <= 0)
463 mpfr_sub_ui (x, x, 180, GFC_RND_MODE);
467 mpfr_ui_sub (x, 360, x, GFC_RND_MODE);
468 mpfr_neg (s, s, GFC_RND_MODE);
473 mpfr_tan (x, x, GFC_RND_MODE);
476 mpfr_mul (x, x, s, GFC_RND_MODE);
481 /* Return NaN for +-Inf and NaN and raise exception. */
483 mpfr_sub (x, x, x, GFC_RND_MODE);
488 ERROR_RETURN(tand, KIND, x);
489 #endif // ENABLE_TAND